mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 06:27:13 -06:00
Compare commits
171 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c29a276188 | ||
|
|
d1694aa8bd | ||
|
|
570cf6fc51 | ||
|
|
8eab86e489 | ||
|
|
566568f61a | ||
|
|
56a73a4d17 | ||
|
|
656d25b75e | ||
|
|
dcdc80ade3 | ||
|
|
09f2d65d5e | ||
|
|
b304d46f7e | ||
|
|
3391f971ec | ||
|
|
778fed27a5 | ||
|
|
1755dbc877 | ||
|
|
4e6f538519 | ||
|
|
a6ecb0ef85 | ||
|
|
438ec6ac5c | ||
|
|
8089e66642 | ||
|
|
d27e0561f2 | ||
|
|
97b21bfa8b | ||
|
|
ec4343ed30 | ||
|
|
948decb3b5 | ||
|
|
d510fee7f6 | ||
|
|
70b7a3d662 | ||
|
|
b142a6f31e | ||
|
|
14705a9b30 | ||
|
|
32b0f1ba10 | ||
|
|
cbffdf4900 | ||
|
|
1238da5e54 | ||
|
|
cba203be09 | ||
|
|
d89c6156b9 | ||
|
|
e06475b3b7 | ||
|
|
5ff99bd45e | ||
|
|
127dd6ac41 | ||
|
|
9e05384513 | ||
|
|
9ffda72ae3 | ||
|
|
72a4c00e72 | ||
|
|
c95e95ef67 | ||
|
|
0c0d4bffbf | ||
|
|
2a494b1d60 | ||
|
|
1463723e52 | ||
|
|
9ade48d078 | ||
|
|
7ba8274fd4 | ||
|
|
238443074c | ||
|
|
0decbec595 | ||
|
|
0eb77c7f72 | ||
|
|
f05df0db0a | ||
|
|
13fbaf1f74 | ||
|
|
969d2642de | ||
|
|
17ce6d39b4 | ||
|
|
261d2b1fe8 | ||
|
|
a54aeb3838 | ||
|
|
cc4f9a9db5 | ||
|
|
a311bac89b | ||
|
|
029f273dd9 | ||
|
|
bbbdd7e4d3 | ||
|
|
3f3ef6b24f | ||
|
|
8c3dfd4a36 | ||
|
|
af57599df9 | ||
|
|
bde25f6ac8 | ||
|
|
c989b97ffa | ||
|
|
5f5c225300 | ||
|
|
36e4071b7f | ||
|
|
1068884bce | ||
|
|
32d102dbc9 | ||
|
|
4e1f092b98 | ||
|
|
bd60a18ff4 | ||
|
|
3b3d7d76eb | ||
|
|
ec76448e9f | ||
|
|
872c84974c | ||
|
|
5dd2008969 | ||
|
|
55ddac2bc7 | ||
|
|
a62dd22f83 | ||
|
|
a503879858 | ||
|
|
14f19dd735 | ||
|
|
9a727f68ce | ||
|
|
d26819d268 | ||
|
|
44752cc9aa | ||
|
|
11e0757387 | ||
|
|
1f1ebc3c44 | ||
|
|
4be97b6ea6 | ||
|
|
3facca5499 | ||
|
|
bfbd25012a | ||
|
|
063fff2ab4 | ||
|
|
fbdc8d5b99 | ||
|
|
625c0a3321 | ||
|
|
2972300112 | ||
|
|
a8e71895ee | ||
|
|
d7a76081e3 | ||
|
|
fd925a6718 | ||
|
|
4fc890a77c | ||
|
|
b804463b73 | ||
|
|
8f161b4b5a | ||
|
|
c6338169f3 | ||
|
|
6cea24ed9e | ||
|
|
3d8eb9eb66 | ||
|
|
a84aceb1ba | ||
|
|
8adb7e3021 | ||
|
|
bc0d5dc9b5 | ||
|
|
1d935d6659 | ||
|
|
445466acd0 | ||
|
|
30af74f806 | ||
|
|
16ddd100d3 | ||
|
|
c946ec170d | ||
|
|
ca514dd76e | ||
|
|
cf44a5c50b | ||
|
|
91b8c02c7f | ||
|
|
ca3b2b4b07 | ||
|
|
722dde63df | ||
|
|
c85baf4dc6 | ||
|
|
96b7770ab2 | ||
|
|
0c00117820 | ||
|
|
3465fa68b4 | ||
|
|
28278a75a7 | ||
|
|
f68a871dd6 | ||
|
|
93d424cfe1 | ||
|
|
a1adde0888 | ||
|
|
13528b49cb | ||
|
|
f3be3f2d1c | ||
|
|
241fe855cc | ||
|
|
ea2447dcb7 | ||
|
|
f40baed65e | ||
|
|
eed11d211b | ||
|
|
19f27a8d56 | ||
|
|
cf3fa17666 | ||
|
|
6fdc56f2d3 | ||
|
|
9f17a5b26d | ||
|
|
fa53e90847 | ||
|
|
50c630f403 | ||
|
|
5630c161ea | ||
|
|
7d16ff9e79 | ||
|
|
a9ea9daec3 | ||
|
|
45bdd40dce | ||
|
|
a2bca88eec | ||
|
|
c0dd02ee13 | ||
|
|
97495a6093 | ||
|
|
4ad45088c4 | ||
|
|
d6d1d4b1b7 | ||
|
|
6e453c170f | ||
|
|
beb2deee52 | ||
|
|
9c69043b2c | ||
|
|
4df34b3f9d | ||
|
|
ee01756188 | ||
|
|
0386aaa18b | ||
|
|
92c4230cde | ||
|
|
26165999e0 | ||
|
|
38b2641078 | ||
|
|
4e19169312 | ||
|
|
46de81c1c9 | ||
|
|
9bf4da7acf | ||
|
|
6f32236fb7 | ||
|
|
c25d857e78 | ||
|
|
8cc2925fd0 | ||
|
|
2b87d3c4db | ||
|
|
7f6f366744 | ||
|
|
b1fdbde5cd | ||
|
|
417f0f5f1c | ||
|
|
ec7673790c | ||
|
|
7d0bdf3b9e | ||
|
|
2ef5270095 | ||
|
|
61ba011c3b | ||
|
|
8d8b9f3e98 | ||
|
|
69899ec29f | ||
|
|
5063621c95 | ||
|
|
030177f739 | ||
|
|
808f5a6381 | ||
|
|
9602b191a9 | ||
|
|
34bd2d781c | ||
|
|
0c604b1023 | ||
|
|
40418607e5 | ||
|
|
5436ea88d8 | ||
|
|
7bec5ec6dc |
36
.github/workflows/ci.yml
vendored
36
.github/workflows/ci.yml
vendored
@@ -9,6 +9,14 @@ on:
|
|||||||
- '*'
|
- '*'
|
||||||
tags:
|
tags:
|
||||||
- '[0-9]*'
|
- '[0-9]*'
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '.*'
|
||||||
|
- '**/.settings/**'
|
||||||
|
- 'flatlaf-core/svg/**'
|
||||||
|
- 'flatlaf-testing/dumps/**'
|
||||||
|
- 'flatlaf-testing/misc/**'
|
||||||
|
- 'images/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -19,27 +27,29 @@ jobs:
|
|||||||
# test against
|
# test against
|
||||||
# - Java 8 (minimum requirement)
|
# - Java 8 (minimum requirement)
|
||||||
# - Java LTS versions (11, 17, ...)
|
# - Java LTS versions (11, 17, ...)
|
||||||
# - lastest Java version(s)
|
# - latest Java version(s)
|
||||||
java:
|
java:
|
||||||
- 8
|
- 8
|
||||||
- 11 # LTS
|
- 11 # LTS
|
||||||
- 17 # LTS
|
- 17 # LTS
|
||||||
|
- 21 # LTS
|
||||||
|
- 23 # latest
|
||||||
toolchain: [""]
|
toolchain: [""]
|
||||||
include:
|
# include:
|
||||||
- java: 17
|
# - java: 21
|
||||||
toolchain: 21 # latest
|
# toolchain: 22 # latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v2
|
||||||
if: matrix.java == '8'
|
if: matrix.java == '8'
|
||||||
|
|
||||||
- name: Setup Java ${{ matrix.java }}
|
- name: Setup Java ${{ matrix.java }}
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: ${{ matrix.java }}
|
java-version: ${{ matrix.java }}
|
||||||
distribution: temurin # Java 8, 11 and 17 are pre-installed on ubuntu-latest
|
distribution: temurin # Java 8, 11, 17 and 21 are pre-installed on ubuntu-latest
|
||||||
cache: gradle
|
cache: gradle
|
||||||
|
|
||||||
- name: Check with Error Prone
|
- name: Check with Error Prone
|
||||||
@@ -50,7 +60,7 @@ jobs:
|
|||||||
run: ./gradlew build -Dtoolchain=${{ matrix.toolchain }}
|
run: ./gradlew build -Dtoolchain=${{ matrix.toolchain }}
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
if: matrix.java == '11'
|
if: matrix.java == '11'
|
||||||
with:
|
with:
|
||||||
name: FlatLaf-build-artifacts
|
name: FlatLaf-build-artifacts
|
||||||
@@ -70,10 +80,10 @@ jobs:
|
|||||||
github.repository == 'JFormDesigner/FlatLaf'
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
distribution: temurin # pre-installed on ubuntu-latest
|
distribution: temurin # pre-installed on ubuntu-latest
|
||||||
@@ -106,10 +116,10 @@ jobs:
|
|||||||
github.repository == 'JFormDesigner/FlatLaf'
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
distribution: temurin # pre-installed on ubuntu-latest
|
distribution: temurin # pre-installed on ubuntu-latest
|
||||||
|
|||||||
6
.github/workflows/fonts.yml
vendored
6
.github/workflows/fonts.yml
vendored
@@ -13,6 +13,8 @@ on:
|
|||||||
- 'flatlaf-fonts/**'
|
- 'flatlaf-fonts/**'
|
||||||
- '.github/workflows/fonts.yml'
|
- '.github/workflows/fonts.yml'
|
||||||
- 'gradle/wrapper/gradle-wrapper.properties'
|
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||||
|
- '!**.md'
|
||||||
|
- '!**/.settings/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Fonts:
|
Fonts:
|
||||||
@@ -30,10 +32,10 @@ jobs:
|
|||||||
github.repository == 'JFormDesigner/FlatLaf'
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
distribution: temurin # pre-installed on ubuntu-latest
|
distribution: temurin # pre-installed on ubuntu-latest
|
||||||
|
|||||||
11
.github/workflows/natives.yml
vendored
11
.github/workflows/natives.yml
vendored
@@ -13,6 +13,8 @@ on:
|
|||||||
- 'flatlaf-natives/**'
|
- 'flatlaf-natives/**'
|
||||||
- '.github/workflows/natives.yml'
|
- '.github/workflows/natives.yml'
|
||||||
- 'gradle/wrapper/gradle-wrapper.properties'
|
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||||
|
- '!**.md'
|
||||||
|
- '!**/.settings/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Natives:
|
Natives:
|
||||||
@@ -20,17 +22,18 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- windows
|
- windows
|
||||||
|
- macos
|
||||||
- ubuntu
|
- ubuntu
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}-latest
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v2
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
@@ -42,7 +45,7 @@ jobs:
|
|||||||
run: ./gradlew build-natives --no-daemon
|
run: ./gradlew build-natives --no-daemon
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
|
name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
37
.github/workflows/pr-snapshots.yml
vendored
Normal file
37
.github/workflows/pr-snapshots.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
|
||||||
|
|
||||||
|
name: PR Snapshots
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '.*'
|
||||||
|
- '**/.settings/**'
|
||||||
|
- 'flatlaf-core/svg/**'
|
||||||
|
- 'flatlaf-testing/dumps/**'
|
||||||
|
- 'flatlaf-testing/misc/**'
|
||||||
|
- 'images/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
snapshot:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Java 11
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
distribution: temurin # pre-installed on ubuntu-latest
|
||||||
|
cache: gradle
|
||||||
|
|
||||||
|
- name: Publish PR snapshot to oss.sonatype.org
|
||||||
|
run: >
|
||||||
|
./gradlew publish -PskipFonts -Dorg.gradle.internal.publish.checksums.insecure=true -Dorg.gradle.parallel=false
|
||||||
|
-Pgithub.event.pull_request.number=${{ github.event.pull_request.number }}
|
||||||
|
env:
|
||||||
|
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||||
|
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||||
230
CHANGELOG.md
230
CHANGELOG.md
@@ -1,6 +1,233 @@
|
|||||||
FlatLaf Change Log
|
FlatLaf Change Log
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
## 3.5.2
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Windows: Fixed repaint issues (ghosting) on some systems (probably depending
|
||||||
|
on graphics card/driver). This is done by setting Java system property
|
||||||
|
`sun.java2d.d3d.onscreen` to `false` (but only if `sun.java2d.d3d.onscreen`,
|
||||||
|
`sun.java2d.d3d` and `sun.java2d.noddraw` are not yet set), which disables
|
||||||
|
usage of Windows Direct3D (DirectX) onscreen surfaces. Component rendering
|
||||||
|
still uses Direct3D. (issue #887)
|
||||||
|
- FlatLaf window decorations:
|
||||||
|
- Iconify/maximize/close buttons did not fill whole title bar height, if some
|
||||||
|
custom component in menu bar increases title bar height. (issue #897)
|
||||||
|
- Windows: Fixed possible application freeze when using custom component that
|
||||||
|
overrides `Component.contains(int x, int y)` and invokes
|
||||||
|
`SwingUtilities.convertPoint()` (or similar) from the overridden method.
|
||||||
|
(issue #878)
|
||||||
|
- TextComponents: Fixed too fast scrolling in multi-line text components when
|
||||||
|
using touchpads (e.g. on macOS). (issue #892)
|
||||||
|
- ToolBar: Fixed endless loop if button in Toolbar has focus and is made
|
||||||
|
invisible. (issue #884)
|
||||||
|
|
||||||
|
#### Other Changes
|
||||||
|
|
||||||
|
- FlatLaf window decorations: Added client property `JRootPane.titleBarHeight`
|
||||||
|
to allow specifying a (larger) preferred height for the title bar. (issue
|
||||||
|
#897)
|
||||||
|
- Added system property `flatlaf.useRoundedPopupBorder` to allow disabling
|
||||||
|
native rounded popup borders on Windows 11 and macOS. On macOS 14.4+, where
|
||||||
|
rounded popup borders are disabled since FlatLaf 3.5 because of occasional
|
||||||
|
problems, you can use this to enable rounded popup borders (at your risk).
|
||||||
|
|
||||||
|
|
||||||
|
## 3.5.1
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- HTML: Fixed occasional cutoff wrapped text when using multi-line text in HTML
|
||||||
|
tags `<h1>`...`<h6>`, `<code>`, `<kbd>`, `<big>`, `<small>` or `<samp>`.
|
||||||
|
(issue #873; regression in 3.5)
|
||||||
|
- Popup: Fixed `UnsupportedOperationException: PERPIXEL_TRANSLUCENT translucency
|
||||||
|
is not supported` exception on Haiku OS when showing popup (partly) outside of
|
||||||
|
window. (issue #869)
|
||||||
|
- HiDPI: Fixed occasional wrong repaint areas when using
|
||||||
|
`HiDPIUtils.installHiDPIRepaintManager()`. (see PR #864)
|
||||||
|
- Added system property `flatlaf.useSubMenuSafeTriangle` to allow disabling
|
||||||
|
submenu safe triangle (PR #490) for
|
||||||
|
[SWTSwing](https://github.com/Chrriis/SWTSwing). (issue #870)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.5
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Table: Support rounded selection. (PR #856)
|
||||||
|
- Button and ToggleButton: Added border colors for pressed and selected states.
|
||||||
|
(issue #848)
|
||||||
|
- Label: Support painting background with rounded corners. (issue #842)
|
||||||
|
- Popup: Fixed flicker of popups (e.g. tooltips) while they are moving (e.g.
|
||||||
|
following mouse pointer). (issues #832 and #672)
|
||||||
|
- FileChooser: Wrap shortcuts in scroll pane. (issue #828)
|
||||||
|
- Theme Editor: On macOS, use larger window title bar. (PR #779)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- macOS: Disabled rounded popup border (see PR #772) on macOS 14.4+ because it
|
||||||
|
may freeze the application and crash the macOS WindowServer process (reports
|
||||||
|
vary from Finder restarts to OS restarts). This is a temporary change until a
|
||||||
|
solution is found. See NetBeans issues
|
||||||
|
[apache/netbeans#7560](https://github.com/apache/netbeans/issues/7560#issuecomment-2226439215)
|
||||||
|
and
|
||||||
|
[apache/netbeans#6647](https://github.com/apache/netbeans/issues/6647#issuecomment-2070124442).
|
||||||
|
- FlatLaf window decorations: Window top border on Windows 10 in "full window
|
||||||
|
content" mode was not fully repainted when activating or deactivating window.
|
||||||
|
(issue #809)
|
||||||
|
- Button and ToggleButton: UI properties `[Toggle]Button.selectedForeground` and
|
||||||
|
`[Toggle]Button.pressedForeground` did not work for HTML text. (issue #848)
|
||||||
|
- HTML: Fixed font sizes for HTML tags `<h1>`...`<h6>`, `<code>`, `<kbd>`,
|
||||||
|
`<big>`, `<small>` and `<samp>` in HTML text for components Button, CheckBox,
|
||||||
|
RadioButton, MenuItem (and subclasses), JideLabel, JideButton, JXBusyLabel and
|
||||||
|
JXHyperlink. Also fixed for Label and ToolTip if using Java 11+.
|
||||||
|
- ScrollPane: Fixed/improved border painting at 125% - 175% scaling to avoid
|
||||||
|
different border thicknesses. (issue #743)
|
||||||
|
- Table: Fixed painting of alternating rows below table if auto-resize mode is
|
||||||
|
`JTable.AUTO_RESIZE_OFF` and table width is smaller than scroll pane (was not
|
||||||
|
updated when table width changed and was painted on wrong side in
|
||||||
|
right-to-left component orientation).
|
||||||
|
- Theme Editor: Fixed occasional empty window on startup on macOS.
|
||||||
|
- FlatLaf window decorations: Fixed black line sometimes painted on top of
|
||||||
|
(native) window border on Windows 11. (issue #852)
|
||||||
|
- HiDPI: Fixed incomplete component paintings at 125% or 175% scaling on Windows
|
||||||
|
where sometimes a 1px wide area at the right or bottom component edge is not
|
||||||
|
repainted. E.g. ScrollPane focus indicator border. (issues #860 and #582)
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- ProgressBar: Log warning (including stack trace) when uninstalling
|
||||||
|
indeterminate progress bar UI or using `JProgressBar.setIndeterminate(false)`
|
||||||
|
not on AWT thread, because this may throw NPE in `FlatProgressBarUI.paint()`.
|
||||||
|
(issues #841 and #830)
|
||||||
|
- Panel: Rounded background of panel with rounded corners is now painted even if
|
||||||
|
panel is not opaque. (issue #840)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.4.1
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- SplitPane: Update divider when client property `JSplitPane.expandableSide`
|
||||||
|
changed.
|
||||||
|
- TabbedPane: Fixed swapped back and forward scroll buttons when using
|
||||||
|
`TabbedPane.scrollButtonsPlacement = trailing` (regression in FlatLaf 3.3).
|
||||||
|
- Fixed missing window top border on Windows 10 in "full window content" mode.
|
||||||
|
(issue #809)
|
||||||
|
- Extras:
|
||||||
|
- `FlatSVGIcon` color filters now support linear gradients. (PR #817)
|
||||||
|
- `FlatSVGIcon`: Use log level `CONFIG` instead of `SEVERE` and allow
|
||||||
|
disabling logging. (issue #823)
|
||||||
|
- Added support for `JSplitPane.expandableSide` client property to
|
||||||
|
`FlatSplitPane`.
|
||||||
|
- Native libraries: Added API version check to test whether native library
|
||||||
|
matches the JAR (bad builds could e.g. ship a newer JAR with an older
|
||||||
|
incompatible native library) and to test whether native methods can be invoked
|
||||||
|
(some security software allows loading native library but blocks method
|
||||||
|
invocation).
|
||||||
|
- macOS: Fixed crash when running in WebSwing. (issue #826; regression in 3.4)
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- File names of custom properties files for nested Laf classes now must include
|
||||||
|
name of enclosing class name. E.g. nested Laf class `IntelliJTheme.ThemeLaf`
|
||||||
|
used `ThemeLaf.properties` in previous versions, but now needs to be named
|
||||||
|
`IntelliJTheme$ThemeLaf.properties`.
|
||||||
|
|
||||||
|
|
||||||
|
## 3.4
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- FlatLaf window decorations (Windows 10/11 and Linux): Support "full window
|
||||||
|
content" mode, which allows you to extend the content into the window title
|
||||||
|
bar. (PR #801)
|
||||||
|
- macOS: Support larger window title bar close/minimize/zoom buttons spacing in
|
||||||
|
[full window content](https://www.formdev.com/flatlaf/macos/#full_window_content)
|
||||||
|
mode and introduced "buttons placeholder". (PR #779)
|
||||||
|
- Native libraries:
|
||||||
|
- System property `flatlaf.nativeLibraryPath` now supports loading native
|
||||||
|
libraries named the same as on Maven central.
|
||||||
|
- Published `flatlaf-<version>-no-natives.jar` to Maven Central. This JAR is
|
||||||
|
equal to `flatlaf-<version>.jar`, except that it does not contain the
|
||||||
|
FlatLaf native libraries. The Maven "classifier" to use this JAR is
|
||||||
|
`no-natives`. You need to distribute the FlatLaf native libraries with your
|
||||||
|
application.
|
||||||
|
See https://www.formdev.com/flatlaf/native-libraries/ for more details.
|
||||||
|
- Improved log messages for loading fails.
|
||||||
|
- Fonts: Updated **Inter** to
|
||||||
|
[v4.0](https://github.com/rsms/inter/releases/tag/v4.0).
|
||||||
|
- Table: Select all text in cell editor when starting editing using `F2` key on
|
||||||
|
Windows or Linux. (issue #652)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- macOS: Setting window background (of undecorated window) to translucent color
|
||||||
|
(alpha < 255) did not show the window translucent. (issue #705)
|
||||||
|
- JIDE CommandMenuBar: Fixed `ClassCastException` when JIDE command bar displays
|
||||||
|
`JideMenu` in popup. (PR #794)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.3
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- macOS (10.14+): Popups (`JPopupMenu`, `JComboBox`, `JToolTip`, etc.) now use
|
||||||
|
native macOS rounded borders. (PR #772; issue #715)
|
||||||
|
- Native libraries: Added `libflatlaf-macos-arm64.dylib` and
|
||||||
|
`libflatlaf-macos-x86_64.dylib`. See also
|
||||||
|
https://www.formdev.com/flatlaf/native-libraries/.
|
||||||
|
- ScrollPane: Support rounded border. (PR #713)
|
||||||
|
- SplitPane: Support divider hover and pressed background colors. (PR #788)
|
||||||
|
- TabbedPane: Support vertical tabs. (PR #758, issue #633)
|
||||||
|
- TabbedPane: Paint rounded tab area background for rounded cards. (issue #717)
|
||||||
|
- ToolBar: Added styling properties `separatorWidth` and `separatorColor`.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Button and ToggleButton: Selected buttons did not use explicitly set
|
||||||
|
foreground color. (issue #756)
|
||||||
|
- FileChooser: Catch NPE in Java 21 when getting icon for `.exe` files that use
|
||||||
|
default Windows exe icon. (see
|
||||||
|
[JDK-8320692](https://bugs.openjdk.org/browse/JDK-8320692))
|
||||||
|
- OptionPane: Fixed styling custom panel background in `JOptionPane`. (issue
|
||||||
|
#761)
|
||||||
|
- ScrollPane: Styling ScrollPane border properties did not work if view
|
||||||
|
component is a Table.
|
||||||
|
- Table:
|
||||||
|
- Switching theme looses table grid and intercell spacing. (issues #733 and
|
||||||
|
#750)
|
||||||
|
- Fixed background of `boolean` columns when using alternating row colors.
|
||||||
|
(issue #780)
|
||||||
|
- Fixed border arc of components in complex table cell editors. (issue #786)
|
||||||
|
- TableHeader:
|
||||||
|
- No longer temporary replace header cell renderer while painting. This avoids
|
||||||
|
a `StackOverflowError` in case that custom renderer does this too. (see
|
||||||
|
[NetBeans issue #6835](https://github.com/apache/netbeans/issues/6835)) This
|
||||||
|
also improves compatibility with custom table header implementations.
|
||||||
|
- Header cell renderer background/foreground colors were not restored after
|
||||||
|
hover if renderer uses `null` for background/foreground. (PR #790)
|
||||||
|
- TabbedPane:
|
||||||
|
- Avoid unnecessary repainting whole tabbed pane content area when layouting
|
||||||
|
leading/trailing components.
|
||||||
|
- Avoid unnecessary repainting of selected tab on temporary changes.
|
||||||
|
- Fixed "endless" layouting and repainting when using nested tabbed panes (top
|
||||||
|
and bottom tab placement) and RSyntaxTextArea (with enabled line-wrapping)
|
||||||
|
as tab content. (see
|
||||||
|
[jadx issue #2030](https://github.com/skylot/jadx/issues/2030))
|
||||||
|
- Fixed broken rendering after resizing window to minimum size and then
|
||||||
|
increasing size again. (issue #767)
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- Removed support for JetBrains custom decorations, which required
|
||||||
|
[JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/wiki) (JBR)
|
||||||
|
8 or 11. It did not work for JBR 17. System property
|
||||||
|
`flatlaf.useJetBrainsCustomDecorations` is now ignored. **Note**: FlatLaf
|
||||||
|
window decorations continue to work with JBR.
|
||||||
|
|
||||||
|
|
||||||
## 3.2.5
|
## 3.2.5
|
||||||
|
|
||||||
#### Fixed bugs
|
#### Fixed bugs
|
||||||
@@ -178,7 +405,6 @@ FlatLaf Change Log
|
|||||||
- Windows DLLs are now digitally signed with FormDev Software GmbH
|
- Windows DLLs are now digitally signed with FormDev Software GmbH
|
||||||
certificate.
|
certificate.
|
||||||
|
|
||||||
|
|
||||||
#### Fixed bugs
|
#### Fixed bugs
|
||||||
|
|
||||||
- FlatLaf window decorations:
|
- FlatLaf window decorations:
|
||||||
@@ -714,7 +940,7 @@ FlatLaf Change Log
|
|||||||
- Native window decorations (Windows 10 only):
|
- Native window decorations (Windows 10 only):
|
||||||
- Fixed occasional application crash in `flatlaf-windows.dll`. (issue #357)
|
- Fixed occasional application crash in `flatlaf-windows.dll`. (issue #357)
|
||||||
- When window is initially shown, fill background with window background color
|
- When window is initially shown, fill background with window background color
|
||||||
(instead of white), which avoids flickering in dark themes. (issue 339)
|
(instead of white), which avoids flickering in dark themes. (issue #339)
|
||||||
- When resizing a window at the right/bottom edge, then first fill the new
|
- When resizing a window at the right/bottom edge, then first fill the new
|
||||||
space with the window background color (instead of black) before the layout
|
space with the window background color (instead of black) before the layout
|
||||||
is updated.
|
is updated.
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -33,14 +33,20 @@ FlatLaf can use 3rd party themes created for IntelliJ Platform (see
|
|||||||
Sponsors
|
Sponsors
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
### Current Sponsors
|
||||||
|
|
||||||
|
[](https://www.formdev.com/flatlaf/sponsor/)
|
||||||
|
|
||||||
|
[Become a Sponsor](https://www.formdev.com/flatlaf/sponsor/)
|
||||||
|
|
||||||
|
### Previous Sponsors
|
||||||
|
|
||||||
<a href="https://www.ej-technologies.com/"><img src="https://www.formdev.com/flatlaf/sponsor/ej-technologies.png" width="200" alt="ej-technologies" title="ej-technologies - Java APM, Java Profiler, Java Installer Builder"></a>
|
<a href="https://www.ej-technologies.com/"><img src="https://www.formdev.com/flatlaf/sponsor/ej-technologies.png" width="200" alt="ej-technologies" title="ej-technologies - Java APM, Java Profiler, Java Installer Builder"></a>
|
||||||
|
|
||||||
<a href="https://www.dbvis.com/"><img src="https://www.formdev.com/flatlaf/sponsor/dbvisualizer.svg" width="200" alt="DbVisualizer" title="DbVisualizer - SQL Client and Editor"></a>
|
<a href="https://www.dbvis.com/"><img src="https://www.formdev.com/flatlaf/sponsor/dbvisualizer.svg" width="200" alt="DbVisualizer" title="DbVisualizer - SQL Client and Editor"></a>
|
||||||
|
|
||||||
<a href="https://www.dscsag.com/"><img src="https://www.formdev.com/flatlaf/sponsor/DSC.png" height="48" alt="DSC Software AG" title="DSC Software AG - Your Companion for Integrative PLM"></a>
|
<a href="https://www.dscsag.com/"><img src="https://www.formdev.com/flatlaf/sponsor/DSC.png" height="48" alt="DSC Software AG" title="DSC Software AG - Your Companion for Integrative PLM"></a>
|
||||||
|
|
||||||
[Become a Sponsor](https://www.formdev.com/flatlaf/sponsor/)
|
|
||||||
|
|
||||||
Demo
|
Demo
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -62,7 +68,7 @@ build script:
|
|||||||
artifactId: flatlaf
|
artifactId: flatlaf
|
||||||
version: (see button below)
|
version: (see button below)
|
||||||
|
|
||||||
Otherwise download `flatlaf-<version>.jar` here:
|
Otherwise, download `flatlaf-<version>.jar` here:
|
||||||
|
|
||||||
[](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf)
|
[](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf)
|
||||||
|
|
||||||
@@ -141,7 +147,6 @@ details and downloads.
|
|||||||
Buzz
|
Buzz
|
||||||
----
|
----
|
||||||
|
|
||||||
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
|
|
||||||
- [FlatLaf 3.1 (and 3.0) announcement on Reddit](https://www.reddit.com/r/java/comments/12xgrsu/flatlaf_31_and_30_swing_look_and_feel/)
|
- [FlatLaf 3.1 (and 3.0) announcement on Reddit](https://www.reddit.com/r/java/comments/12xgrsu/flatlaf_31_and_30_swing_look_and_feel/)
|
||||||
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
|
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
|
||||||
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
|
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
|
||||||
@@ -312,6 +317,9 @@ Applications using FlatLaf
|
|||||||
|
|
||||||
-  
|
-  
|
||||||
[BGBlitz](https://www.bgblitz.com/) (**commercial**) - professional Backgammon
|
[BGBlitz](https://www.bgblitz.com/) (**commercial**) - professional Backgammon
|
||||||
|
-  [MCreator](https://github.com/MCreator/MCreator) -
|
||||||
|
software used to make Minecraft Java Edition mods, Minecraft Bedrock Edition Add-Ons,
|
||||||
|
and data packs without programming knowledge
|
||||||
-  [MapTool](https://github.com/RPTools/maptool) - virtual
|
-  [MapTool](https://github.com/RPTools/maptool) - virtual
|
||||||
Tabletop for playing role-playing games
|
Tabletop for playing role-playing games
|
||||||
- [MegaMek](https://github.com/MegaMek/megamek),
|
- [MegaMek](https://github.com/MegaMek/megamek),
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ import net.ltgt.gradle.errorprone.errorprone
|
|||||||
|
|
||||||
version = property( if( hasProperty( "release" ) ) "flatlaf.releaseVersion" else "flatlaf.developmentVersion" ) as String
|
version = property( if( hasProperty( "release" ) ) "flatlaf.releaseVersion" else "flatlaf.developmentVersion" ) as String
|
||||||
|
|
||||||
|
// for PR snapshots change version to 'PR-<pr_number>-SNAPSHOT'
|
||||||
|
val pullRequestNumber = findProperty( "github.event.pull_request.number" )
|
||||||
|
if( pullRequestNumber != null )
|
||||||
|
version = "PR-${pullRequestNumber}-SNAPSHOT"
|
||||||
|
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
version = rootProject.version
|
version = rootProject.version
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class ReorderJarEntries
|
|||||||
// 1st pass: copy .properties files
|
// 1st pass: copy .properties files
|
||||||
copyFiles( zipOutStream, jarFile, name -> name.endsWith( ".properties" ) );
|
copyFiles( zipOutStream, jarFile, name -> name.endsWith( ".properties" ) );
|
||||||
|
|
||||||
// 2st pass: copy other files
|
// 2nd pass: copy other files
|
||||||
copyFiles( zipOutStream, jarFile, name -> !name.endsWith( ".properties" ) );
|
copyFiles( zipOutStream, jarFile, name -> !name.endsWith( ".properties" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ library {
|
|||||||
}
|
}
|
||||||
with( linkTask.get() ) {
|
with( linkTask.get() ) {
|
||||||
if( name.contains( "Release" ) )
|
if( name.contains( "Release" ) )
|
||||||
debuggable.set( false )
|
debuggable = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,34 +44,34 @@ publishing {
|
|||||||
|
|
||||||
pom {
|
pom {
|
||||||
afterEvaluate {
|
afterEvaluate {
|
||||||
this@pom.name.set( extension.name )
|
this@pom.name = extension.name
|
||||||
this@pom.description.set( extension.description )
|
this@pom.description = extension.description
|
||||||
}
|
}
|
||||||
url.set( "https://github.com/JFormDesigner/FlatLaf" )
|
url = "https://github.com/JFormDesigner/FlatLaf"
|
||||||
|
|
||||||
licenses {
|
licenses {
|
||||||
license {
|
license {
|
||||||
name.set( "The Apache License, Version 2.0" )
|
name = "The Apache License, Version 2.0"
|
||||||
url.set( "https://www.apache.org/licenses/LICENSE-2.0.txt" )
|
url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
developers {
|
developers {
|
||||||
developer {
|
developer {
|
||||||
name.set( "Karl Tauber" )
|
name = "Karl Tauber"
|
||||||
organization.set( "FormDev Software GmbH" )
|
organization = "FormDev Software GmbH"
|
||||||
organizationUrl.set( "https://www.formdev.com/" )
|
organizationUrl = "https://www.formdev.com/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scm {
|
scm {
|
||||||
connection.set( "scm:git:git://github.com/JFormDesigner/FlatLaf.git" )
|
connection = "scm:git:git://github.com/JFormDesigner/FlatLaf.git"
|
||||||
url.set( "https://github.com/JFormDesigner/FlatLaf" )
|
url = "https://github.com/JFormDesigner/FlatLaf"
|
||||||
}
|
}
|
||||||
|
|
||||||
issueManagement {
|
issueManagement {
|
||||||
system.set( "GitHub" )
|
system = "GitHub"
|
||||||
url.set( "https://github.com/JFormDesigner/FlatLaf/issues" )
|
url = "https://github.com/JFormDesigner/FlatLaf/issues"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ tasks.withType<Sign>().configureEach {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check whether parallel build is enabled
|
// check whether parallel build is enabled
|
||||||
tasks.withType<PublishToMavenRepository>().configureEach {
|
tasks.withType<AbstractPublishToMaven>().configureEach {
|
||||||
doFirst {
|
doFirst {
|
||||||
if( System.getProperty( "org.gradle.parallel" ) == "true" )
|
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'." )
|
throw RuntimeException( "Publishing does not work correctly with enabled parallel build. Disable parallel build with VM option '-Dorg.gradle.parallel=false'." )
|
||||||
|
|||||||
@@ -21,6 +21,6 @@ plugins {
|
|||||||
val toolchainJavaVersion = System.getProperty( "toolchain" )
|
val toolchainJavaVersion = System.getProperty( "toolchain" )
|
||||||
if( !toolchainJavaVersion.isNullOrEmpty() ) {
|
if( !toolchainJavaVersion.isNullOrEmpty() ) {
|
||||||
java.toolchain {
|
java.toolchain {
|
||||||
languageVersion.set( JavaLanguageVersion.of( toolchainJavaVersion ) )
|
languageVersion = JavaLanguageVersion.of( toolchainJavaVersion )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ plugins {
|
|||||||
val sigtest = configurations.create( "sigtest" )
|
val sigtest = configurations.create( "sigtest" )
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation( libs.bundles.junit )
|
testImplementation( libs.junit )
|
||||||
testRuntimeOnly( libs.junit.engine )
|
testRuntimeOnly( libs.junit.launcher )
|
||||||
|
|
||||||
// https://github.com/jtulach/netbeans-apitest
|
// https://github.com/jtulach/netbeans-apitest
|
||||||
sigtest( libs.sigtest )
|
sigtest( libs.sigtest )
|
||||||
@@ -42,11 +42,11 @@ java {
|
|||||||
tasks {
|
tasks {
|
||||||
compileJava {
|
compileJava {
|
||||||
// generate JNI headers
|
// generate JNI headers
|
||||||
options.headerOutputDirectory.set( layout.buildDirectory.dir( "generated/jni-headers" ) )
|
options.headerOutputDirectory = layout.buildDirectory.dir( "generated/jni-headers" )
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName = "flatlaf"
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
ReorderJarEntries.reorderJarEntries( outputs.files.singleFile );
|
ReorderJarEntries.reorderJarEntries( outputs.files.singleFile );
|
||||||
@@ -54,11 +54,32 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
named<Jar>( "sourcesJar" ) {
|
named<Jar>( "sourcesJar" ) {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName = "flatlaf"
|
||||||
}
|
}
|
||||||
|
|
||||||
named<Jar>( "javadocJar" ) {
|
named<Jar>( "javadocJar" ) {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName = "flatlaf"
|
||||||
|
}
|
||||||
|
|
||||||
|
register<Zip>( "jarNoNatives" ) {
|
||||||
|
group = "build"
|
||||||
|
dependsOn( "jar" )
|
||||||
|
|
||||||
|
archiveBaseName = "flatlaf"
|
||||||
|
archiveClassifier = "no-natives"
|
||||||
|
archiveExtension = "jar"
|
||||||
|
destinationDirectory = layout.buildDirectory.dir( "libs" )
|
||||||
|
|
||||||
|
from( zipTree( jar.get().archiveFile.get().asFile ) )
|
||||||
|
exclude( "com/formdev/flatlaf/natives/**" )
|
||||||
|
}
|
||||||
|
|
||||||
|
withType<AbstractPublishToMaven>().configureEach {
|
||||||
|
dependsOn( "jarNoNatives" )
|
||||||
|
}
|
||||||
|
|
||||||
|
withType<Sign>().configureEach {
|
||||||
|
dependsOn( "jarNoNatives" )
|
||||||
}
|
}
|
||||||
|
|
||||||
check {
|
check {
|
||||||
@@ -127,9 +148,13 @@ flatlafPublish {
|
|||||||
|
|
||||||
val natives = "src/main/resources/com/formdev/flatlaf/natives"
|
val natives = "src/main/resources/com/formdev/flatlaf/natives"
|
||||||
nativeArtifacts = listOf(
|
nativeArtifacts = listOf(
|
||||||
|
NativeArtifact( tasks.getByName( "jarNoNatives" ).outputs.files.asPath, "no-natives", "jar" ),
|
||||||
|
|
||||||
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
|
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
|
||||||
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
|
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
|
||||||
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),
|
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),
|
||||||
|
NativeArtifact( "${natives}/libflatlaf-macos-arm64.dylib", "macos-arm64", "dylib" ),
|
||||||
|
NativeArtifact( "${natives}/libflatlaf-macos-x86_64.dylib", "macos-x86_64", "dylib" ),
|
||||||
NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ),
|
NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#Signature file v4.1
|
#Signature file v4.1
|
||||||
#Version 3.2.4
|
#Version 3.5.2
|
||||||
|
|
||||||
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
|
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
|
||||||
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
|
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
|
||||||
@@ -12,7 +12,13 @@ fld public final static java.lang.String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarBu
|
|||||||
fld public final static java.lang.String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner"
|
fld public final static java.lang.String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner"
|
||||||
fld public final static java.lang.String COMPONENT_ROUND_RECT = "JComponent.roundRect"
|
fld public final static java.lang.String COMPONENT_ROUND_RECT = "JComponent.roundRect"
|
||||||
fld public final static java.lang.String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption"
|
fld public final static java.lang.String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption"
|
||||||
|
fld public final static java.lang.String FULL_WINDOW_CONTENT = "FlatLaf.fullWindowContent"
|
||||||
|
fld public final static java.lang.String FULL_WINDOW_CONTENT_BUTTONS_BOUNDS = "FlatLaf.fullWindowContent.buttonsBounds"
|
||||||
|
fld public final static java.lang.String FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER = "FlatLaf.fullWindowContent.buttonsPlaceholder"
|
||||||
fld public final static java.lang.String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight"
|
fld public final static java.lang.String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight"
|
||||||
|
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING = "FlatLaf.macOS.windowButtonsSpacing"
|
||||||
|
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING_LARGE = "large"
|
||||||
|
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING_MEDIUM = "medium"
|
||||||
fld public final static java.lang.String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"
|
fld public final static java.lang.String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"
|
||||||
fld public final static java.lang.String MINIMUM_HEIGHT = "JComponent.minimumHeight"
|
fld public final static java.lang.String MINIMUM_HEIGHT = "JComponent.minimumHeight"
|
||||||
fld public final static java.lang.String MINIMUM_WIDTH = "JComponent.minimumWidth"
|
fld public final static java.lang.String MINIMUM_WIDTH = "JComponent.minimumWidth"
|
||||||
@@ -23,6 +29,7 @@ fld public final static java.lang.String PLACEHOLDER_TEXT = "JTextField.placehol
|
|||||||
fld public final static java.lang.String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius"
|
fld public final static java.lang.String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius"
|
||||||
fld public final static java.lang.String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted"
|
fld public final static java.lang.String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted"
|
||||||
fld public final static java.lang.String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight"
|
fld public final static java.lang.String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight"
|
||||||
|
fld public final static java.lang.String POPUP_ROUNDED_BORDER_WIDTH = "Popup.roundedBorderWidth"
|
||||||
fld public final static java.lang.String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight"
|
fld public final static java.lang.String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight"
|
||||||
fld public final static java.lang.String PROGRESS_BAR_SQUARE = "JProgressBar.square"
|
fld public final static java.lang.String PROGRESS_BAR_SQUARE = "JProgressBar.square"
|
||||||
fld public final static java.lang.String SCROLL_BAR_SHOW_BUTTONS = "JScrollBar.showButtons"
|
fld public final static java.lang.String SCROLL_BAR_SHOW_BUTTONS = "JScrollBar.showButtons"
|
||||||
@@ -67,6 +74,11 @@ fld public final static java.lang.String TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT = "JT
|
|||||||
fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight"
|
fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement"
|
fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets"
|
fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets"
|
||||||
|
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION = "JTabbedPane.tabRotation"
|
||||||
|
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_AUTO = "auto"
|
||||||
|
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_LEFT = "left"
|
||||||
|
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_NONE = "none"
|
||||||
|
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_RIGHT = "right"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType"
|
fld public final static java.lang.String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_CARD = "card"
|
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_CARD = "card"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined"
|
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined"
|
||||||
@@ -88,6 +100,7 @@ fld public final static java.lang.String TEXT_FIELD_TRAILING_COMPONENT = "JTextF
|
|||||||
fld public final static java.lang.String TEXT_FIELD_TRAILING_ICON = "JTextField.trailingIcon"
|
fld public final static java.lang.String TEXT_FIELD_TRAILING_ICON = "JTextField.trailingIcon"
|
||||||
fld public final static java.lang.String TITLE_BAR_BACKGROUND = "JRootPane.titleBarBackground"
|
fld public final static java.lang.String TITLE_BAR_BACKGROUND = "JRootPane.titleBarBackground"
|
||||||
fld public final static java.lang.String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground"
|
fld public final static java.lang.String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground"
|
||||||
|
fld public final static java.lang.String TITLE_BAR_HEIGHT = "JRootPane.titleBarHeight"
|
||||||
fld public final static java.lang.String TITLE_BAR_SHOW_CLOSE = "JRootPane.titleBarShowClose"
|
fld public final static java.lang.String TITLE_BAR_SHOW_CLOSE = "JRootPane.titleBarShowClose"
|
||||||
fld public final static java.lang.String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon"
|
fld public final static java.lang.String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon"
|
||||||
fld public final static java.lang.String TITLE_BAR_SHOW_ICONIFFY = "JRootPane.titleBarShowIconify"
|
fld public final static java.lang.String TITLE_BAR_SHOW_ICONIFFY = "JRootPane.titleBarShowIconify"
|
||||||
@@ -211,6 +224,7 @@ meth public static java.util.Map<java.lang.String,java.lang.Class<?>> getStyleab
|
|||||||
meth public static java.util.Map<java.lang.String,java.lang.String> getGlobalExtraDefaults()
|
meth public static java.util.Map<java.lang.String,java.lang.String> getGlobalExtraDefaults()
|
||||||
meth public static java.util.function.Function<java.lang.String,java.awt.Color> getSystemColorGetter()
|
meth public static java.util.function.Function<java.lang.String,java.awt.Color> getSystemColorGetter()
|
||||||
meth public static javax.swing.UIDefaults$ActiveValue createActiveFontValue(float)
|
meth public static javax.swing.UIDefaults$ActiveValue createActiveFontValue(float)
|
||||||
|
meth public static void disableWindowsD3Donscreen()
|
||||||
meth public static void hideMnemonics()
|
meth public static void hideMnemonics()
|
||||||
meth public static void initIconColors(javax.swing.UIDefaults,boolean)
|
meth public static void initIconColors(javax.swing.UIDefaults,boolean)
|
||||||
meth public static void installLafInfo(java.lang.String,java.lang.Class<? extends javax.swing.LookAndFeel>)
|
meth public static void installLafInfo(java.lang.String,java.lang.Class<? extends javax.swing.LookAndFeel>)
|
||||||
@@ -241,7 +255,7 @@ meth public void setExtraDefaults(java.util.Map<java.lang.String,java.lang.Strin
|
|||||||
meth public void uninitialize()
|
meth public void uninitialize()
|
||||||
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
|
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
|
||||||
supr javax.swing.plaf.basic.BasicLookAndFeel
|
supr javax.swing.plaf.basic.BasicLookAndFeel
|
||||||
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,getUIMethod,getUIMethodInitialized,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,preferredFontFamily,preferredLightFontFamily,preferredMonospacedFontFamily,preferredSemiboldFontFamily,subMenuUsabilityHelperInstalled,systemColorGetter,uiDefaultsGetters,updateUIPending
|
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,preferredFontFamily,preferredLightFontFamily,preferredMonospacedFontFamily,preferredSemiboldFontFamily,subMenuUsabilityHelperInstalled,systemColorGetter,uiDefaultsGetters,updateUIPending
|
||||||
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
|
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
|
||||||
|
|
||||||
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
|
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
|
||||||
@@ -282,7 +296,10 @@ fld public final static java.lang.String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.ui
|
|||||||
fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled"
|
fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled"
|
||||||
fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange"
|
fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange"
|
||||||
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
|
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
|
||||||
|
anno 0 java.lang.Deprecated()
|
||||||
fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"
|
fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"
|
||||||
|
fld public final static java.lang.String USE_ROUNDED_POPUP_BORDER = "flatlaf.useRoundedPopupBorder"
|
||||||
|
fld public final static java.lang.String USE_SUB_MENU_SAFE_TRIANGLE = "flatlaf.useSubMenuSafeTriangle"
|
||||||
fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"
|
fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"
|
||||||
fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont"
|
fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont"
|
||||||
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations"
|
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations"
|
||||||
@@ -616,16 +633,33 @@ hfds alpha,hsl,rgb
|
|||||||
|
|
||||||
CLSS public com.formdev.flatlaf.util.HiDPIUtils
|
CLSS public com.formdev.flatlaf.util.HiDPIUtils
|
||||||
cons public init()
|
cons public init()
|
||||||
|
innr public abstract interface static DirtyRegionCallback
|
||||||
innr public abstract interface static Painter
|
innr public abstract interface static Painter
|
||||||
|
innr public static HiDPIRepaintManager
|
||||||
meth public static float computeTextYCorrection(java.awt.Graphics2D)
|
meth public static float computeTextYCorrection(java.awt.Graphics2D)
|
||||||
meth public static java.awt.Graphics2D createGraphicsTextYCorrection(java.awt.Graphics2D)
|
meth public static java.awt.Graphics2D createGraphicsTextYCorrection(java.awt.Graphics2D)
|
||||||
|
meth public static void addDirtyRegion(javax.swing.JComponent,int,int,int,int,com.formdev.flatlaf.util.HiDPIUtils$DirtyRegionCallback)
|
||||||
meth public static void drawStringUnderlineCharAtWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int,int)
|
meth public static void drawStringUnderlineCharAtWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int,int)
|
||||||
meth public static void drawStringWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int)
|
meth public static void drawStringWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int)
|
||||||
|
meth public static void installHiDPIRepaintManager()
|
||||||
meth public static void paintAtScale1x(java.awt.Graphics2D,int,int,int,int,com.formdev.flatlaf.util.HiDPIUtils$Painter)
|
meth public static void paintAtScale1x(java.awt.Graphics2D,int,int,int,int,com.formdev.flatlaf.util.HiDPIUtils$Painter)
|
||||||
meth public static void paintAtScale1x(java.awt.Graphics2D,javax.swing.JComponent,com.formdev.flatlaf.util.HiDPIUtils$Painter)
|
meth public static void paintAtScale1x(java.awt.Graphics2D,javax.swing.JComponent,com.formdev.flatlaf.util.HiDPIUtils$Painter)
|
||||||
|
meth public static void repaint(java.awt.Component)
|
||||||
|
meth public static void repaint(java.awt.Component,int,int,int,int)
|
||||||
|
meth public static void repaint(java.awt.Component,java.awt.Rectangle)
|
||||||
supr java.lang.Object
|
supr java.lang.Object
|
||||||
hfds CORRECTION_INTER,CORRECTION_OPEN_SANS,CORRECTION_SEGOE_UI,CORRECTION_TAHOMA,SCALE_FACTORS,useDebugScaleFactor,useTextYCorrection
|
hfds CORRECTION_INTER,CORRECTION_OPEN_SANS,CORRECTION_SEGOE_UI,CORRECTION_TAHOMA,SCALE_FACTORS,useDebugScaleFactor,useTextYCorrection
|
||||||
|
|
||||||
|
CLSS public abstract interface static com.formdev.flatlaf.util.HiDPIUtils$DirtyRegionCallback
|
||||||
|
outer com.formdev.flatlaf.util.HiDPIUtils
|
||||||
|
meth public abstract void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
|
||||||
|
|
||||||
|
CLSS public static com.formdev.flatlaf.util.HiDPIUtils$HiDPIRepaintManager
|
||||||
|
outer com.formdev.flatlaf.util.HiDPIUtils
|
||||||
|
cons public init()
|
||||||
|
meth public void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
|
||||||
|
supr javax.swing.RepaintManager
|
||||||
|
|
||||||
CLSS public abstract interface static com.formdev.flatlaf.util.HiDPIUtils$Painter
|
CLSS public abstract interface static com.formdev.flatlaf.util.HiDPIUtils$Painter
|
||||||
outer com.formdev.flatlaf.util.HiDPIUtils
|
outer com.formdev.flatlaf.util.HiDPIUtils
|
||||||
meth public abstract void paint(java.awt.Graphics2D,int,int,int,int,double)
|
meth public abstract void paint(java.awt.Graphics2D,int,int,int,int,double)
|
||||||
@@ -1124,6 +1158,31 @@ meth public void provideErrorFeedback(java.awt.Component)
|
|||||||
meth public void uninitialize()
|
meth public void uninitialize()
|
||||||
supr java.lang.Object
|
supr java.lang.Object
|
||||||
|
|
||||||
|
CLSS public javax.swing.RepaintManager
|
||||||
|
cons public init()
|
||||||
|
meth public boolean isCompletelyDirty(javax.swing.JComponent)
|
||||||
|
meth public boolean isDoubleBufferingEnabled()
|
||||||
|
meth public java.awt.Dimension getDoubleBufferMaximumSize()
|
||||||
|
meth public java.awt.Image getOffscreenBuffer(java.awt.Component,int,int)
|
||||||
|
meth public java.awt.Image getVolatileOffscreenBuffer(java.awt.Component,int,int)
|
||||||
|
meth public java.awt.Rectangle getDirtyRegion(javax.swing.JComponent)
|
||||||
|
meth public java.lang.String toString()
|
||||||
|
meth public static javax.swing.RepaintManager currentManager(java.awt.Component)
|
||||||
|
meth public static javax.swing.RepaintManager currentManager(javax.swing.JComponent)
|
||||||
|
meth public static void setCurrentManager(javax.swing.RepaintManager)
|
||||||
|
meth public void addDirtyRegion(java.applet.Applet,int,int,int,int)
|
||||||
|
meth public void addDirtyRegion(java.awt.Window,int,int,int,int)
|
||||||
|
meth public void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
|
||||||
|
meth public void addInvalidComponent(javax.swing.JComponent)
|
||||||
|
meth public void markCompletelyClean(javax.swing.JComponent)
|
||||||
|
meth public void markCompletelyDirty(javax.swing.JComponent)
|
||||||
|
meth public void paintDirtyRegions()
|
||||||
|
meth public void removeInvalidComponent(javax.swing.JComponent)
|
||||||
|
meth public void setDoubleBufferMaximumSize(java.awt.Dimension)
|
||||||
|
meth public void setDoubleBufferingEnabled(boolean)
|
||||||
|
meth public void validateInvalidComponents()
|
||||||
|
supr java.lang.Object
|
||||||
|
|
||||||
CLSS public abstract javax.swing.border.AbstractBorder
|
CLSS public abstract javax.swing.border.AbstractBorder
|
||||||
cons public init()
|
cons public init()
|
||||||
intf java.io.Serializable
|
intf java.io.Serializable
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public interface FlatClientProperties
|
|||||||
//---- JButton ------------------------------------------------------------
|
//---- JButton ------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies type of a button.
|
* Specifies type of button.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
@@ -102,6 +102,17 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String BUTTON_TYPE_BORDERLESS = "borderless";
|
String BUTTON_TYPE_BORDERLESS = "borderless";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the button preferred size will be made square (quadratically).
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String SQUARE_SIZE = "JButton.squareSize";
|
||||||
|
|
||||||
|
|
||||||
|
//---- JCheckBox ----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies selected state of a checkbox.
|
* Specifies selected state of a checkbox.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -118,14 +129,6 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String SELECTED_STATE_INDETERMINATE = "indeterminate";
|
String SELECTED_STATE_INDETERMINATE = "indeterminate";
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies whether the button preferred size will be made square (quadratically).
|
|
||||||
* <p>
|
|
||||||
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
|
||||||
*/
|
|
||||||
String SQUARE_SIZE = "JButton.squareSize";
|
|
||||||
|
|
||||||
|
|
||||||
//---- JComponent ---------------------------------------------------------
|
//---- JComponent ---------------------------------------------------------
|
||||||
|
|
||||||
@@ -257,19 +260,116 @@ public interface FlatClientProperties
|
|||||||
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
|
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether a component in an embedded menu bar should behave as caption
|
* Specifies whether a component shown in a window title bar area should behave as caption
|
||||||
* (left-click allows moving window, right-click shows window system menu).
|
* (left-click allows moving window, right-click shows window system menu).
|
||||||
* The component does not receive mouse pressed/released/clicked/dragged events,
|
* The caption component does not receive mouse pressed/released/clicked/dragged events,
|
||||||
* but it gets mouse entered/exited/moved events.
|
* but it gets mouse entered/exited/moved events.
|
||||||
* <p>
|
* <p>
|
||||||
|
* Since 3.4, this client property also supports using a function that can check
|
||||||
|
* whether a given location in the component should behave as caption.
|
||||||
|
* Useful for components that do not use mouse input on whole component bounds.
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* myComponent.putClientProperty( "JComponent.titleBarCaption",
|
||||||
|
* (Function<Point, Boolean>) pt -> {
|
||||||
|
* // parameter pt contains mouse location (in myComponent coordinates)
|
||||||
|
* // return true if the component is not interested in mouse input at the given location
|
||||||
|
* // return false if the component wants process mouse input at the given location
|
||||||
|
* // return null if the component children should be checked
|
||||||
|
* return ...; // check here
|
||||||
|
* } );
|
||||||
|
* }</pre>
|
||||||
|
* <b>Warning</b>:
|
||||||
|
* <ul>
|
||||||
|
* <li>This function is invoked often when mouse is moved over window title bar area
|
||||||
|
* and should therefore return quickly.
|
||||||
|
* <li>This function is invoked on 'AWT-Windows' thread (not 'AWT-EventQueue' thread)
|
||||||
|
* while processing Windows messages.
|
||||||
|
* It <b>must not</b> change any component property or layout because this could cause a dead lock.
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean} or {@link java.util.function.Function}<Point, Boolean>
|
||||||
*
|
*
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
|
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
|
||||||
|
|
||||||
|
|
||||||
|
//---- Panel --------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the panel as placeholder for the iconfify/maximize/close buttons
|
||||||
|
* in fullWindowContent mode. See {@link #FULL_WINDOW_CONTENT}.
|
||||||
|
* <p>
|
||||||
|
* If fullWindowContent mode is enabled, the preferred size of the panel is equal
|
||||||
|
* to the size of the iconfify/maximize/close buttons. Otherwise is is {@code 0,0}.
|
||||||
|
* <p>
|
||||||
|
* You're responsible to layout that panel at the top-left or top-right corner,
|
||||||
|
* depending on platform, where the iconfify/maximize/close buttons are located.
|
||||||
|
* <p>
|
||||||
|
* Syntax of the value string is: {@code "win|mac [horizontal|vertical] [zeroInFullScreen] [leftToRight|rightToLeft]"}.
|
||||||
|
* <p>
|
||||||
|
* The string must start with {@code "win"} (for Windows or Linux) or
|
||||||
|
* with {@code "mac"} (for macOS) and specifies the platform where the placeholder
|
||||||
|
* should be used. On macOS, you need the placeholder in the top-left corner,
|
||||||
|
* but on Windows/Linux you need it in the top-right corner. So if your application supports
|
||||||
|
* fullWindowContent mode on both platforms, you can add two placeholders to your layout
|
||||||
|
* and FlatLaf automatically uses only one of them. The other gets size {@code 0,0}.
|
||||||
|
* <p>
|
||||||
|
* Optionally, you can append following options to the value string (separated by space characters):
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code "horizontal"} - preferred height is zero
|
||||||
|
* <li>{@code "vertical"} - preferred width is zero
|
||||||
|
* <li>{@code "zeroInFullScreen"} - in full-screen mode on macOS, preferred size is {@code 0,0}
|
||||||
|
* <li>{@code "leftToRight"} - in right-to-left component orientation, preferred size is {@code 0,0}
|
||||||
|
* <li>{@code "rightToLeft"} - in left-to-right component orientation, preferred size is {@code 0,0}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* Example for adding placeholder to top-left corner on macOS:
|
||||||
|
* <pre>{@code
|
||||||
|
* JPanel placeholder = new JPanel();
|
||||||
|
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
|
||||||
|
*
|
||||||
|
* JToolBar toolBar = new JToolBar();
|
||||||
|
* // add tool bar items
|
||||||
|
*
|
||||||
|
* JPanel toolBarPanel = new JPanel( new BorderLayout() );
|
||||||
|
* toolBarPanel.add( placeholder, BorderLayout.WEST );
|
||||||
|
* toolBarPanel.add( toolBar, BorderLayout.CENTER );
|
||||||
|
*
|
||||||
|
* frame.getContentPane().add( toolBarPanel, BorderLayout.NORTH );
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* Or add placeholder as first item to the tool bar:
|
||||||
|
* <pre>{@code
|
||||||
|
* JPanel placeholder = new JPanel();
|
||||||
|
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
|
||||||
|
*
|
||||||
|
* JToolBar toolBar = new JToolBar();
|
||||||
|
* toolBar.add( placeholder );
|
||||||
|
* // add tool bar items
|
||||||
|
*
|
||||||
|
* frame.getContentPane().add( toolBar, BorderLayout.NORTH );
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* If a tabbed pane is located at the top, you can add the placeholder
|
||||||
|
* as leading component to that tabbed pane:
|
||||||
|
* <pre>{@code
|
||||||
|
* JPanel placeholder = new JPanel();
|
||||||
|
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
|
||||||
|
*
|
||||||
|
* tabbedPane.putClientProperty( FlatClientProperties.TABBED_PANE_LEADING_COMPONENT, placeholder );
|
||||||
|
* }</pre>
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JPanel}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER = "FlatLaf.fullWindowContent.buttonsPlaceholder";
|
||||||
|
|
||||||
|
|
||||||
//---- Popup --------------------------------------------------------------
|
//---- Popup --------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,12 +378,13 @@ public interface FlatClientProperties
|
|||||||
* <p>
|
* <p>
|
||||||
* Note that this is not available on all platforms since it requires special support.
|
* Note that this is not available on all platforms since it requires special support.
|
||||||
* Supported platforms:
|
* Supported platforms:
|
||||||
* <p>
|
* <ul>
|
||||||
* <strong>Windows 11</strong> (x86 or x86_64): Only two corner radiuses are supported
|
* <li><strong>Windows 11</strong>: Only two corner radiuses are supported
|
||||||
* by the OS: {@code DWMWCP_ROUND} is 8px and {@code DWMWCP_ROUNDSMALL} is 4px.
|
* 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 this value is {@code 1 - 4}, then {@code DWMWCP_ROUNDSMALL} is used.
|
||||||
* If it is {@code >= 5}, then {@code DWMWCP_ROUND} is used.
|
* If it is {@code >= 5}, then {@code DWMWCP_ROUND} is used.
|
||||||
* <p>
|
* <li><strong>macOS</strong> (10.14 and later): Any corner radius is supported.
|
||||||
|
* </ul>
|
||||||
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||||
*
|
*
|
||||||
@@ -291,6 +392,24 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius";
|
String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the popup rounded border width if the component is shown in a popup
|
||||||
|
* or if the component is the owner of another component that is shown in a popup.
|
||||||
|
* <p>
|
||||||
|
* Only used if popup uses rounded border.
|
||||||
|
* <p>
|
||||||
|
* Note that this is not available on all platforms since it requires special support.
|
||||||
|
* Supported platforms:
|
||||||
|
* <ul>
|
||||||
|
* <li><strong>macOS</strong> (10.14 and later)
|
||||||
|
* </ul>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.Float}<br>
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String POPUP_ROUNDED_BORDER_WIDTH = "Popup.roundedBorderWidth";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether a drop shadow is painted if the component is shown in a popup
|
* Specifies whether a drop shadow is painted if the component is shown in a popup
|
||||||
* or if the component is the owner of another component that is shown in a popup.
|
* or if the component is the owner of another component that is shown in a popup.
|
||||||
@@ -342,7 +461,7 @@ public interface FlatClientProperties
|
|||||||
* {@link FlatSystemProperties#USE_WINDOW_DECORATIONS}, but higher priority
|
* {@link FlatSystemProperties#USE_WINDOW_DECORATIONS}, but higher priority
|
||||||
* than UI default {@code TitlePane.useWindowDecorations}.
|
* than UI default {@code TitlePane.useWindowDecorations}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
@@ -362,13 +481,55 @@ public interface FlatClientProperties
|
|||||||
* {@link FlatSystemProperties#MENUBAR_EMBEDDED}, but higher priority
|
* {@link FlatSystemProperties#MENUBAR_EMBEDDED}, but higher priority
|
||||||
* than UI default {@code TitlePane.menuBarEmbedded}.
|
* than UI default {@code TitlePane.menuBarEmbedded}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*/
|
*/
|
||||||
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
|
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the content pane (and the glass pane) should be extended
|
||||||
|
* into the window title bar area
|
||||||
|
* (requires enabled window decorations). Default is {@code false}.
|
||||||
|
* <p>
|
||||||
|
* On macOS, use client property {@code apple.awt.fullWindowContent}
|
||||||
|
* (see <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">macOS Full window content</a>).
|
||||||
|
* <p>
|
||||||
|
* Setting this enables/disables full window content
|
||||||
|
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
||||||
|
* <p>
|
||||||
|
* If {@code true}, the content pane (and the glass pane) is extended into
|
||||||
|
* the title bar area. The window icon and title are hidden.
|
||||||
|
* Only the iconfify/maximize/close buttons stay visible in the upper right corner
|
||||||
|
* (and overlap the content pane).
|
||||||
|
* <p>
|
||||||
|
* The user can left-click-and-drag on the title bar area to move the window,
|
||||||
|
* except when clicking on a component that processes mouse events (e.g. buttons or menus).
|
||||||
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String FULL_WINDOW_CONTENT = "FlatLaf.fullWindowContent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the current bounds of the iconfify/maximize/close buttons
|
||||||
|
* (in root pane coordinates) if fullWindowContent mode is enabled.
|
||||||
|
* Otherwise its value is {@code null}.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: Do not set this client property. It is set by FlatLaf.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.awt.Rectangle}
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String FULL_WINDOW_CONTENT_BUTTONS_BOUNDS = "FlatLaf.fullWindowContent.buttonsBounds";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the window icon should be shown in the window title bar
|
* Specifies whether the window icon should be shown in the window title bar
|
||||||
* (requires enabled window decorations). Default is UI property {@code TitlePane.showIcon}.
|
* (requires enabled window decorations). Default is UI property {@code TitlePane.showIcon}.
|
||||||
@@ -378,7 +539,7 @@ public interface FlatClientProperties
|
|||||||
* <p>
|
* <p>
|
||||||
* This client property has higher priority than UI default {@code TitlePane.showIcon}.
|
* This client property has higher priority than UI default {@code TitlePane.showIcon}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
@@ -394,6 +555,8 @@ public interface FlatClientProperties
|
|||||||
* Setting this shows/hides the windows title
|
* Setting this shows/hides the windows title
|
||||||
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
||||||
* <p>
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*
|
*
|
||||||
@@ -402,12 +565,14 @@ public interface FlatClientProperties
|
|||||||
String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle";
|
String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the "iconfify" button should be shown in the window title bar
|
* Specifies whether the "iconify" button should be shown in the window title bar
|
||||||
* (requires enabled window decorations). Default is {@code true}.
|
* (requires enabled window decorations). Default is {@code true}.
|
||||||
* <p>
|
* <p>
|
||||||
* Setting this shows/hides the "iconfify" button
|
* Setting this shows/hides the "iconify" button
|
||||||
* for the {@code JFrame} that contains the root pane.
|
* for the {@code JFrame} that contains the root pane.
|
||||||
* <p>
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*
|
*
|
||||||
@@ -422,6 +587,8 @@ public interface FlatClientProperties
|
|||||||
* Setting this shows/hides the "maximize/restore" button
|
* Setting this shows/hides the "maximize/restore" button
|
||||||
* for the {@code JFrame} that contains the root pane.
|
* for the {@code JFrame} that contains the root pane.
|
||||||
* <p>
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*
|
*
|
||||||
@@ -436,6 +603,8 @@ public interface FlatClientProperties
|
|||||||
* Setting this shows/hides the "close" button
|
* Setting this shows/hides the "close" button
|
||||||
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
||||||
* <p>
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*
|
*
|
||||||
@@ -446,7 +615,7 @@ public interface FlatClientProperties
|
|||||||
/**
|
/**
|
||||||
* Background color of window title bar (requires enabled window decorations).
|
* Background color of window title bar (requires enabled window decorations).
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.awt.Color}
|
* <strong>Value type</strong> {@link java.awt.Color}
|
||||||
@@ -458,7 +627,7 @@ public interface FlatClientProperties
|
|||||||
/**
|
/**
|
||||||
* Foreground color of window title bar (requires enabled window decorations).
|
* Foreground color of window title bar (requires enabled window decorations).
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.awt.Color}
|
* <strong>Value type</strong> {@link java.awt.Color}
|
||||||
@@ -467,10 +636,24 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground";
|
String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the preferred height of title bar (requires enabled window decorations).
|
||||||
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
String TITLE_BAR_HEIGHT = "JRootPane.titleBarHeight";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the glass pane should have full height and overlap the title bar,
|
* Specifies whether the glass pane should have full height and overlap the title bar,
|
||||||
* if FlatLaf window decorations are enabled. Default is {@code false}.
|
* if FlatLaf window decorations are enabled. Default is {@code false}.
|
||||||
* <p>
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*
|
*
|
||||||
@@ -487,7 +670,7 @@ public interface FlatClientProperties
|
|||||||
* On macOS, Java supports this out of the box.
|
* On macOS, Java supports this out of the box.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that this client property must be set before the window becomes displayable.
|
* Note that this client property must be set before the window becomes displayable.
|
||||||
* Otherwise an {@link IllegalComponentStateException} is thrown.
|
* Otherwise, an {@link IllegalComponentStateException} is thrown.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
@@ -828,7 +1011,7 @@ public interface FlatClientProperties
|
|||||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
|
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
|
||||||
* <strong>Allowed Values</strong>
|
* <strong>Allowed Values</strong>
|
||||||
* {@link SwingConstants#LEADING} (default)
|
* {@link SwingConstants#LEADING} (default),
|
||||||
* {@link SwingConstants#TRAILING},
|
* {@link SwingConstants#TRAILING},
|
||||||
* {@link SwingConstants#CENTER},
|
* {@link SwingConstants#CENTER},
|
||||||
* {@link #TABBED_PANE_ALIGN_LEADING} (default),
|
* {@link #TABBED_PANE_ALIGN_LEADING} (default),
|
||||||
@@ -932,6 +1115,59 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement";
|
String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the rotation of the tabs (title, icon, etc.).
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link SwingConstants#LEFT},
|
||||||
|
* {@link SwingConstants#RIGHT},
|
||||||
|
* {@link #TABBED_PANE_TAB_ROTATION_NONE} (default),
|
||||||
|
* {@link #TABBED_PANE_TAB_ROTATION_AUTO},
|
||||||
|
* {@link #TABBED_PANE_TAB_ROTATION_LEFT} or
|
||||||
|
* {@link #TABBED_PANE_TAB_ROTATION_RIGHT}
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ROTATION = "JTabbedPane.tabRotation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs are not rotated.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_ROTATION
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ROTATION_NONE = "none";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs are rotated depending on tab placement.
|
||||||
|
* <p>
|
||||||
|
* For top and bottom tab placement, the tabs are not rotated.<br>
|
||||||
|
* For left tab placement, the tabs are rotated counter-clockwise.<br>
|
||||||
|
* For right tab placement, the tabs are rotated clockwise.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_ROTATION
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ROTATION_AUTO = "auto";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs are rotated counter-clockwise.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_ROTATION
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ROTATION_LEFT = "left";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs are rotated clockwise.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_ROTATION
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ROTATION_RIGHT = "right";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies a component that will be placed at the leading edge of the tabs area.
|
* Specifies a component that will be placed at the leading edge of the tabs area.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -1136,8 +1372,8 @@ public interface FlatClientProperties
|
|||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||||
* <strong>SupportedValues:</strong>
|
* <strong>Allowed Values</strong>
|
||||||
* {@link SwingConstants#BOTTOM} (default)
|
* {@link SwingConstants#BOTTOM} (default),
|
||||||
* {@link SwingConstants#TOP},
|
* {@link SwingConstants#TOP},
|
||||||
* {@link SwingConstants#LEFT} or
|
* {@link SwingConstants#LEFT} or
|
||||||
* {@link SwingConstants#RIGHT}
|
* {@link SwingConstants#RIGHT}
|
||||||
@@ -1191,6 +1427,44 @@ public interface FlatClientProperties
|
|||||||
String TREE_PAINT_SELECTION = "JTree.paintSelection";
|
String TREE_PAINT_SELECTION = "JTree.paintSelection";
|
||||||
|
|
||||||
|
|
||||||
|
//---- macOS --------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the spacing around the macOS window close/minimize/zoom buttons.
|
||||||
|
* Useful if <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">full window content</a>
|
||||||
|
* is enabled.
|
||||||
|
* <p>
|
||||||
|
* (requires macOS 10.14+ for "medium" spacing and macOS 11+ for "large" spacing, requires Java 17+)
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #MACOS_WINDOW_BUTTONS_SPACING_MEDIUM} or
|
||||||
|
* {@link #MACOS_WINDOW_BUTTONS_SPACING_LARGE} (requires macOS 11+)
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String MACOS_WINDOW_BUTTONS_SPACING = "FlatLaf.macOS.windowButtonsSpacing";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add medium spacing around the macOS window close/minimize/zoom buttons.
|
||||||
|
*
|
||||||
|
* @see #MACOS_WINDOW_BUTTONS_SPACING
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String MACOS_WINDOW_BUTTONS_SPACING_MEDIUM = "medium";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add large spacing around the macOS window close/minimize/zoom buttons.
|
||||||
|
* <p>
|
||||||
|
* (requires macOS 11+; "medium" is used on older systems)
|
||||||
|
*
|
||||||
|
* @see #MACOS_WINDOW_BUTTONS_SPACING
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String MACOS_WINDOW_BUTTONS_SPACING_LARGE = "large";
|
||||||
|
|
||||||
|
|
||||||
//---- helper methods -----------------------------------------------------
|
//---- helper methods -----------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public abstract class FlatDefaultsAddon
|
|||||||
public InputStream getDefaults( Class<?> lafClass ) {
|
public InputStream getDefaults( Class<?> lafClass ) {
|
||||||
Class<?> addonClass = this.getClass();
|
Class<?> addonClass = this.getClass();
|
||||||
String propertiesName = '/' + addonClass.getPackage().getName().replace( '.', '/' )
|
String propertiesName = '/' + addonClass.getPackage().getName().replace( '.', '/' )
|
||||||
+ '/' + lafClass.getSimpleName() + ".properties";
|
+ '/' + UIDefaultsLoader.simpleClassName( lafClass ) + ".properties";
|
||||||
return addonClass.getResourceAsStream( propertiesName );
|
return addonClass.getResourceAsStream( propertiesName );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class FlatInputMaps
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// join ltr and rtl bindings to fix up/down/etc keys in right-to-left component orientation
|
// join ltr and rtl bindings to fix up/down/etc. keys in right-to-left component orientation
|
||||||
Object[] bindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings" );
|
Object[] bindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings" );
|
||||||
Object[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" );
|
Object[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" );
|
||||||
if( bindings != null && rtlBindings != null ) {
|
if( bindings != null && rtlBindings != null ) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
@@ -30,9 +31,6 @@ import java.awt.image.ImageProducer;
|
|||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.invoke.MethodHandle;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.invoke.MethodType;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -78,6 +76,7 @@ import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
|
|||||||
import com.formdev.flatlaf.ui.FlatPopupFactory;
|
import com.formdev.flatlaf.ui.FlatPopupFactory;
|
||||||
import com.formdev.flatlaf.ui.FlatRootPaneUI;
|
import com.formdev.flatlaf.ui.FlatRootPaneUI;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.ui.JavaCompatibility2;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.FontUtils;
|
import com.formdev.flatlaf.util.FontUtils;
|
||||||
import com.formdev.flatlaf.util.GrayFilter;
|
import com.formdev.flatlaf.util.GrayFilter;
|
||||||
@@ -121,6 +120,46 @@ public abstract class FlatLaf
|
|||||||
private static String preferredSemiboldFontFamily;
|
private static String preferredSemiboldFontFamily;
|
||||||
private static String preferredMonospacedFontFamily;
|
private static String preferredMonospacedFontFamily;
|
||||||
|
|
||||||
|
static {
|
||||||
|
// see disableWindowsD3Donscreen() for details
|
||||||
|
// https://github.com/JFormDesigner/FlatLaf/issues/887
|
||||||
|
if( SystemInfo.isWindows &&
|
||||||
|
System.getProperty( "sun.java2d.d3d.onscreen" ) == null &&
|
||||||
|
System.getProperty( "sun.java2d.d3d" ) == null &&
|
||||||
|
System.getProperty( "sun.java2d.noddraw" ) == null )
|
||||||
|
System.setProperty( "sun.java2d.d3d.onscreen", "false" );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable usage of Windows Direct3D (DirectX) onscreen surfaces because this may lead to
|
||||||
|
* repaint issues (ghosting) on some systems (probably depending on graphics card/driver).
|
||||||
|
* Problem occurs usually when a small heavy-weight popup window (menu, combobox, tooltip) is shown.
|
||||||
|
* <p>
|
||||||
|
* Sets system property {@code sun.java2d.d3d.onscreen} to {@code false},
|
||||||
|
* but only if {@code sun.java2d.d3d.onscreen}, {@code sun.java2d.d3d}
|
||||||
|
* and {@code sun.java2d.noddraw} are not yet set.
|
||||||
|
* <p>
|
||||||
|
* <strong>Note</strong>: Must be invoked very early before the graphics environment is created.
|
||||||
|
* <p>
|
||||||
|
* This method is automatically invoked when loading this class,
|
||||||
|
* which is usually before the graphics environment is created.
|
||||||
|
* E.g. when doing {@code FlatLightLaf.setup()} or
|
||||||
|
* {@code UIManager.setLookAndFeel( "com.formdev.flatlaf.FlatLightLaf" )}.
|
||||||
|
* <p>
|
||||||
|
* However, it may be invoked too late if you use some methods from {@link UIManager}
|
||||||
|
* of {@link GraphicsEnvironment} before setting look and feel.
|
||||||
|
* E.g. {@link UIManager#put(Object, Object)}.
|
||||||
|
* In that case invoke this method yourself very early.
|
||||||
|
* <p>
|
||||||
|
* <strong>Tip</strong>: How to find out when the graphics environment is created?
|
||||||
|
* Set a breakpoint at constructor of class {@link GraphicsEnvironment} and look at the stack.
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
public static void disableWindowsD3Donscreen() {
|
||||||
|
// dummy method used to trigger invocation of "static {...}" block
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the application look and feel to the given LaF
|
* Sets the application look and feel to the given LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
@@ -183,17 +222,11 @@ public abstract class FlatLaf
|
|||||||
* This depends on the operating system and on the used Java runtime.
|
* This depends on the operating system and on the used Java runtime.
|
||||||
* <p>
|
* <p>
|
||||||
* This method returns {@code true} on Windows 10/11 (see exception below)
|
* This method returns {@code true} on Windows 10/11 (see exception below)
|
||||||
* and on Linux, {@code false} otherwise.
|
* and on Linux, otherwise returns {@code false}.
|
||||||
|
* <p>
|
||||||
|
* Returns also {@code false} on Windows 10/11 if
|
||||||
|
* FlatLaf native window border support is available (requires Windows 10/11).
|
||||||
* <p>
|
* <p>
|
||||||
* Returns also {@code false} on Windows 10/11 if:
|
|
||||||
* <ul>
|
|
||||||
* <li>FlatLaf native window border support is available (requires Windows 10/11)</li>
|
|
||||||
* <li>running in
|
|
||||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
|
||||||
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
|
||||||
* and JBR supports custom window decorations
|
|
||||||
* </li>
|
|
||||||
* </ul>
|
|
||||||
* In these cases, custom decorations are enabled by the root pane.
|
* In these cases, custom decorations are enabled by the root pane.
|
||||||
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
|
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
|
||||||
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
|
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
|
||||||
@@ -1295,8 +1328,8 @@ public abstract class FlatLaf
|
|||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
public static Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public static Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
StyleableUI ui = getStyleableUI( c );
|
ComponentUI ui = JavaCompatibility2.getUI( c );
|
||||||
return (ui != null) ? ui.getStyleableInfos( c ) : null;
|
return (ui instanceof StyleableUI) ? ((StyleableUI)ui).getStyleableInfos( c ) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1308,41 +1341,10 @@ public abstract class FlatLaf
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
public static <T> T getStyleableValue( JComponent c, String key ) {
|
public static <T> T getStyleableValue( JComponent c, String key ) {
|
||||||
StyleableUI ui = getStyleableUI( c );
|
ComponentUI ui = JavaCompatibility2.getUI( c );
|
||||||
return (ui != null) ? (T) ui.getStyleableValue( c, key ) : null;
|
return (ui instanceof StyleableUI) ? (T) ((StyleableUI)ui).getStyleableValue( c, key ) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StyleableUI getStyleableUI( JComponent c ) {
|
|
||||||
if( !getUIMethodInitialized ) {
|
|
||||||
getUIMethodInitialized = true;
|
|
||||||
|
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
|
||||||
try {
|
|
||||||
// JComponent.getUI() is available since Java 9
|
|
||||||
getUIMethod = MethodHandles.lookup().findVirtual( JComponent.class, "getUI",
|
|
||||||
MethodType.methodType( ComponentUI.class ) );
|
|
||||||
} catch( Exception ex ) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Object ui;
|
|
||||||
if( getUIMethod != null )
|
|
||||||
ui = getUIMethod.invoke( c );
|
|
||||||
else
|
|
||||||
ui = c.getClass().getMethod( "getUI" ).invoke( c );
|
|
||||||
return (ui instanceof StyleableUI) ? (StyleableUI) ui : null;
|
|
||||||
} catch( Throwable ex ) {
|
|
||||||
// ignore
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean getUIMethodInitialized;
|
|
||||||
private static MethodHandle getUIMethod;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the preferred font family to be used for (nearly) all fonts; or {@code null}.
|
* Returns the preferred font family to be used for (nearly) all fonts; or {@code null}.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public interface FlatSystemProperties
|
|||||||
* {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and
|
* {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and
|
||||||
* UI default {@code TitlePane.useWindowDecorations}.
|
* UI default {@code TitlePane.useWindowDecorations}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10/11)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
* <strong>Default</strong> none
|
* <strong>Default</strong> none
|
||||||
@@ -99,11 +99,14 @@ public interface FlatSystemProperties
|
|||||||
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
|
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
|
||||||
* Then FlatLaf native window decorations are used.
|
* Then FlatLaf native window decorations are used.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10/11)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
* <strong>Default</strong> {@code false} (since v2; was {@code true} in v1)
|
* <strong>Default</strong> {@code false} (since v2; was {@code true} in v1)
|
||||||
|
*
|
||||||
|
* @deprecated No longer used since FlatLaf 3.3. Retained for API compatibility.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,7 +120,7 @@ public interface FlatSystemProperties
|
|||||||
* {@link FlatClientProperties#MENU_BAR_EMBEDDED} and
|
* {@link FlatClientProperties#MENU_BAR_EMBEDDED} and
|
||||||
* UI default {@code TitlePane.menuBarEmbedded}.
|
* UI default {@code TitlePane.menuBarEmbedded}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10/11)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
* <strong>Default</strong> none
|
* <strong>Default</strong> none
|
||||||
@@ -132,6 +135,18 @@ public interface FlatSystemProperties
|
|||||||
*/
|
*/
|
||||||
String ANIMATION = "flatlaf.animation";
|
String ANIMATION = "flatlaf.animation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether native rounded popup borders should be used (if supported by operating system).
|
||||||
|
* <p>
|
||||||
|
* (requires Window 11 or macOS)
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> {@code true}; except on macOS 14.4+ where it is {@code false}
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
String USE_ROUNDED_POPUP_BORDER = "flatlaf.useRoundedPopupBorder";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
|
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -169,24 +184,48 @@ public interface FlatSystemProperties
|
|||||||
String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary";
|
String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies a directory in which the native FlatLaf libraries have been extracted.
|
* Specifies a directory in which the FlatLaf native libraries are searched for.
|
||||||
* The path can be absolute or relative to current application working directory.
|
* The path can be absolute or relative to current application working directory.
|
||||||
* This can be used to avoid extraction of the native libraries to the temporary directory at runtime.
|
* This can be used to avoid extraction of the native libraries to the temporary directory at runtime.
|
||||||
* <p>
|
* <p>
|
||||||
* If the value is {@code "system"}, then {@link System#loadLibrary(String)} is
|
* If the value is {@code "system"} (supported since FlatLaf 2.6),
|
||||||
* used to load the native library.
|
* then {@link System#loadLibrary(String)} is used to load the native library.
|
||||||
* Searches for the native library in classloader of caller
|
* This searches for the native library in classloader of caller
|
||||||
* (using {@link ClassLoader#findLibrary(String)}) and in paths specified
|
* (using {@link ClassLoader#findLibrary(String)}) and in paths specified
|
||||||
* in system properties {@code sun.boot.library.path} and {@code java.library.path}.
|
* in system properties {@code sun.boot.library.path} and {@code java.library.path}.
|
||||||
* (supported since FlatLaf 2.6)
|
|
||||||
* <p>
|
* <p>
|
||||||
* If the native library can not loaded from the given path (or via {@link System#loadLibrary(String)}),
|
* If the native library can not be loaded from the given path (or via {@link System#loadLibrary(String)}),
|
||||||
* then the embedded native library is extracted to the temporary directory and loaded from there.
|
* then the embedded native library is extracted to the temporary directory and loaded from there.
|
||||||
|
* <p>
|
||||||
|
* The file names of the native libraries must be either:
|
||||||
|
* <ul>
|
||||||
|
* <li>the same as in flatlaf.jar in package 'com/formdev/flatlaf/natives' (required for "system") or
|
||||||
|
* <li>when downloaded from Maven central then as described here:
|
||||||
|
* <a href="https://www.formdev.com/flatlaf/native-libraries/">https://www.formdev.com/flatlaf/native-libraries/</a>
|
||||||
|
* (requires FlatLaf 3.4)
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* <strong>Note</strong>: Since FlatLaf 3.1 it is recommended to download the
|
||||||
|
* FlatLaf native libraries from Maven central and distribute them with your
|
||||||
|
* application in the same directory as flatlaf.jar.
|
||||||
|
* Then it is <strong>not necessary</strong> to set this system property.
|
||||||
|
* See <a href="https://www.formdev.com/flatlaf/native-libraries/">https://www.formdev.com/flatlaf/native-libraries/</a>
|
||||||
|
* for details.
|
||||||
*
|
*
|
||||||
* @since 2
|
* @since 2
|
||||||
*/
|
*/
|
||||||
String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath";
|
String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether safe triangle is used to improve usability of submenus.
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> {@code true}
|
||||||
|
*
|
||||||
|
* @since 3.5.1
|
||||||
|
*/
|
||||||
|
String USE_SUB_MENU_SAFE_TRIANGLE = "flatlaf.useSubMenuSafeTriangle";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a system property is set and returns {@code true} if its value
|
* Checks whether a system property is set and returns {@code true} if its value
|
||||||
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}.
|
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}.
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ class LinuxFontPolicy
|
|||||||
* Gets the default font for KDE from KDE configuration files.
|
* Gets the default font for KDE from KDE configuration files.
|
||||||
*
|
*
|
||||||
* The Swing fonts are not updated when the user changes system font size
|
* The Swing fonts are not updated when the user changes system font size
|
||||||
* (System Settings > Fonts > Force Font DPI). A application restart is necessary.
|
* (System Settings > Fonts > Force Font DPI). An application restart is necessary.
|
||||||
* This is the same behavior as in native KDE applications.
|
* This is the same behavior as in native KDE applications.
|
||||||
*
|
*
|
||||||
* The "display scale factor" (kdeglobals: [KScreen] > ScaleFactor) is not used
|
* The "display scale factor" (kdeglobals: [KScreen] > ScaleFactor) is not used
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.event.ChangeEvent;
|
import javax.swing.event.ChangeEvent;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Improves usability of submenus by using a
|
* Improves usability of submenus by using a
|
||||||
@@ -64,6 +65,7 @@ class SubMenuUsabilityHelper
|
|||||||
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
|
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
|
||||||
private static SubMenuUsabilityHelper instance;
|
private static SubMenuUsabilityHelper instance;
|
||||||
|
|
||||||
|
private boolean eventQueuePushNotSupported;
|
||||||
private SubMenuEventQueue subMenuEventQueue;
|
private SubMenuEventQueue subMenuEventQueue;
|
||||||
private SafeTrianglePainter safeTrianglePainter;
|
private SafeTrianglePainter safeTrianglePainter;
|
||||||
private boolean changePending;
|
private boolean changePending;
|
||||||
@@ -83,6 +85,9 @@ class SubMenuUsabilityHelper
|
|||||||
if( instance != null )
|
if( instance != null )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_SUB_MENU_SAFE_TRIANGLE, true ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
instance = new SubMenuUsabilityHelper();
|
instance = new SubMenuUsabilityHelper();
|
||||||
MenuSelectionManager.defaultManager().addChangeListener( instance );
|
MenuSelectionManager.defaultManager().addChangeListener( instance );
|
||||||
return true;
|
return true;
|
||||||
@@ -99,7 +104,7 @@ class SubMenuUsabilityHelper
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stateChanged( ChangeEvent e ) {
|
public void stateChanged( ChangeEvent e ) {
|
||||||
if( !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
|
if( eventQueuePushNotSupported || !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// handle menu selection change later, but only once in case of temporary changes
|
// handle menu selection change later, but only once in case of temporary changes
|
||||||
@@ -172,9 +177,30 @@ debug*/
|
|||||||
targetTopY = popupLocation.y;
|
targetTopY = popupLocation.y;
|
||||||
targetBottomY = popupLocation.y + popupSize.height;
|
targetBottomY = popupLocation.y + popupSize.height;
|
||||||
|
|
||||||
// install own event queue to supress mouse events when mouse is moved within safe triangle
|
// install own event queue to suppress mouse events when mouse is moved within safe triangle
|
||||||
if( subMenuEventQueue == null )
|
if( subMenuEventQueue == null ) {
|
||||||
subMenuEventQueue = new SubMenuEventQueue();
|
SubMenuEventQueue queue = new SubMenuEventQueue();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
|
toolkit.getSystemEventQueue().push( queue );
|
||||||
|
|
||||||
|
// check whether push() worked
|
||||||
|
// (e.g. SWTSwing uses own event queue that does not support push())
|
||||||
|
if( toolkit.getSystemEventQueue() != queue ) {
|
||||||
|
eventQueuePushNotSupported = true;
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to push submenu event queue. Disabling submenu safe triangle.", null );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subMenuEventQueue = queue;
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
// catch runtime exception from EventQueue.push()
|
||||||
|
eventQueuePushNotSupported = true;
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to push submenu event queue. Disabling submenu safe triangle.", ex );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create safe triangle painter
|
// create safe triangle painter
|
||||||
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
|
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
|
||||||
@@ -247,8 +273,6 @@ debug*/
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
timeoutTimer.setRepeats( false );
|
timeoutTimer.setRepeats( false );
|
||||||
|
|
||||||
Toolkit.getDefaultToolkit().getSystemEventQueue().push( this );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void uninstall() {
|
void uninstall() {
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ class UIDefaultsLoader
|
|||||||
classLoader = FlatLaf.class.getClassLoader();
|
classLoader = FlatLaf.class.getClassLoader();
|
||||||
|
|
||||||
for( Class<?> lafClass : lafClasses ) {
|
for( Class<?> lafClass : lafClasses ) {
|
||||||
String propertiesName = packageName + '/' + lafClass.getSimpleName() + ".properties";
|
String propertiesName = packageName + '/' + simpleClassName( lafClass ) + ".properties";
|
||||||
try( InputStream in = classLoader.getResourceAsStream( propertiesName ) ) {
|
try( InputStream in = classLoader.getResourceAsStream( propertiesName ) ) {
|
||||||
if( in != null )
|
if( in != null )
|
||||||
properties.load( in );
|
properties.load( in );
|
||||||
@@ -171,7 +171,7 @@ class UIDefaultsLoader
|
|||||||
// load from package URL
|
// load from package URL
|
||||||
URL packageUrl = (URL) source;
|
URL packageUrl = (URL) source;
|
||||||
for( Class<?> lafClass : lafClasses ) {
|
for( Class<?> lafClass : lafClasses ) {
|
||||||
URL propertiesUrl = new URL( packageUrl + lafClass.getSimpleName() + ".properties" );
|
URL propertiesUrl = new URL( packageUrl + simpleClassName( lafClass ) + ".properties" );
|
||||||
|
|
||||||
try( InputStream in = propertiesUrl.openStream() ) {
|
try( InputStream in = propertiesUrl.openStream() ) {
|
||||||
properties.load( in );
|
properties.load( in );
|
||||||
@@ -183,7 +183,7 @@ class UIDefaultsLoader
|
|||||||
// load from folder
|
// load from folder
|
||||||
File folder = (File) source;
|
File folder = (File) source;
|
||||||
for( Class<?> lafClass : lafClasses ) {
|
for( Class<?> lafClass : lafClasses ) {
|
||||||
File propertiesFile = new File( folder, lafClass.getSimpleName() + ".properties" );
|
File propertiesFile = new File( folder, simpleClassName( lafClass ) + ".properties" );
|
||||||
if( !propertiesFile.isFile() )
|
if( !propertiesFile.isFile() )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -294,6 +294,14 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to Class.getSimpleName(), but includes enclosing class for nested classes.
|
||||||
|
*/
|
||||||
|
static String simpleClassName( Class<?> cls ) {
|
||||||
|
String className = cls.getName();
|
||||||
|
return className.substring( className.lastIndexOf( '.' ) + 1 );
|
||||||
|
}
|
||||||
|
|
||||||
static void logParseError( String key, String value, RuntimeException ex, boolean severe ) {
|
static void logParseError( String key, String value, RuntimeException ex, boolean severe ) {
|
||||||
String message = "FlatLaf: Failed to parse: '" + key + '=' + value + '\'';
|
String message = "FlatLaf: Failed to parse: '" + key + '=' + value + '\'';
|
||||||
if( severe )
|
if( severe )
|
||||||
@@ -630,14 +638,18 @@ class UIDefaultsLoader
|
|||||||
// Syntax: top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
|
// Syntax: top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
|
||||||
List<String> parts = splitFunctionParams( value, ',' );
|
List<String> parts = splitFunctionParams( value, ',' );
|
||||||
Insets insets = parseInsets( value );
|
Insets insets = parseInsets( value );
|
||||||
ColorUIResource lineColor = (parts.size() >= 5)
|
ColorUIResource lineColor = (parts.size() >= 5 && !parts.get( 4 ).isEmpty())
|
||||||
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver )
|
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver )
|
||||||
: null;
|
: null;
|
||||||
float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty()) ? parseFloat( parts.get( 5 ) ) : 1f;
|
float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty())
|
||||||
int arc = (parts.size() >= 7) ? parseInteger( parts.get( 6 ) ) : 0;
|
? parseFloat( parts.get( 5 ) )
|
||||||
|
: 1f;
|
||||||
|
int arc = (parts.size() >= 7) && !parts.get( 6 ).isEmpty()
|
||||||
|
? parseInteger( parts.get( 6 ) )
|
||||||
|
: -1;
|
||||||
|
|
||||||
return (LazyValue) t -> {
|
return (LazyValue) t -> {
|
||||||
return (lineColor != null)
|
return (lineColor != null || arc > 0)
|
||||||
? new FlatLineBorder( insets, lineColor, lineThickness, arc )
|
? new FlatLineBorder( insets, lineColor, lineThickness, arc )
|
||||||
: new FlatEmptyBorder( insets );
|
: new FlatEmptyBorder( insets );
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ public abstract class FlatAbstractIcon
|
|||||||
// g2.setColor( Color.blue );
|
// g2.setColor( Color.blue );
|
||||||
// g2.drawRect( x, y, getIconWidth() - 1, getIconHeight() - 1 );
|
// g2.drawRect( x, y, getIconWidth() - 1, getIconHeight() - 1 );
|
||||||
|
|
||||||
|
paintBackground( c, g2, x, y );
|
||||||
|
|
||||||
g2.translate( x, y );
|
g2.translate( x, y );
|
||||||
UIScale.scaleGraphics( g2 );
|
UIScale.scaleGraphics( g2 );
|
||||||
|
|
||||||
@@ -69,7 +71,11 @@ public abstract class FlatAbstractIcon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void paintIcon( Component c, Graphics2D g2 );
|
/** @since 3.5.2 */
|
||||||
|
protected void paintBackground( Component c, Graphics2D g, int x, int y ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void paintIcon( Component c, Graphics2D g );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIconWidth() {
|
public int getIconWidth() {
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class FlatTabbedPaneCloseIcon
|
|||||||
closeSize.width, closeSize.height, closeArc, closeArc );
|
closeSize.width, closeSize.height, closeArc, closeArc );
|
||||||
}
|
}
|
||||||
|
|
||||||
// set cross color
|
// set color of cross
|
||||||
Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
|
Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
|
||||||
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
|
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
|
||||||
|
|
||||||
|
|||||||
@@ -57,11 +57,11 @@ public class FlatTreeOpenIcon
|
|||||||
double arc = 1.5;
|
double arc = 1.5;
|
||||||
double arc2 = 0.5;
|
double arc2 = 0.5;
|
||||||
path = FlatUIUtils.createPath( false,
|
path = FlatUIUtils.createPath( false,
|
||||||
// bottom-left of opend part
|
// bottom-left of opened part
|
||||||
2,13.5,
|
2,13.5,
|
||||||
// top-left of opend part
|
// top-left of opened part
|
||||||
FlatUIUtils.ROUNDED, 4.5,7.5, arc,
|
FlatUIUtils.ROUNDED, 4.5,7.5, arc,
|
||||||
// top-right of opend part
|
// top-right of opened part
|
||||||
FlatUIUtils.ROUNDED, 15.5,7.5, arc2,
|
FlatUIUtils.ROUNDED, 15.5,7.5, arc2,
|
||||||
|
|
||||||
// bottom-right
|
// bottom-right
|
||||||
|
|||||||
@@ -60,23 +60,24 @@ public abstract class FlatWindowAbstractIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
paintBackground( c, g );
|
|
||||||
|
|
||||||
g.setColor( getForeground( c ) );
|
g.setColor( getForeground( c ) );
|
||||||
HiDPIUtils.paintAtScale1x( g, 0, 0, width, height, this::paintIconAt1x );
|
HiDPIUtils.paintAtScale1x( g, 0, 0, width, height, this::paintIconAt1x );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor );
|
protected abstract void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor );
|
||||||
|
|
||||||
protected void paintBackground( Component c, Graphics2D g ) {
|
/** @since 3.5.2 */
|
||||||
|
@Override
|
||||||
|
protected void paintBackground( Component c, Graphics2D g, int x, int y ) {
|
||||||
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
|
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
|
||||||
if( background != null ) {
|
if( background != null ) {
|
||||||
// disable antialiasing for background rectangle painting to avoid blury edges when scaled (e.g. at 125% or 175%)
|
// disable antialiasing for background rectangle painting to avoid blurry edges when scaled (e.g. at 125% or 175%)
|
||||||
Object oldHint = g.getRenderingHint( RenderingHints.KEY_ANTIALIASING );
|
Object oldHint = g.getRenderingHint( RenderingHints.KEY_ANTIALIASING );
|
||||||
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
|
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
|
||||||
|
|
||||||
|
// fill background of whole component
|
||||||
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
|
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
|
||||||
g.fillRect( 0, 0, width, height );
|
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
|
|
||||||
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldHint );
|
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldHint );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import javax.swing.JComboBox;
|
|||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JSpinner;
|
import javax.swing.JSpinner;
|
||||||
import javax.swing.JViewport;
|
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.basic.BasicBorders;
|
import javax.swing.plaf.basic.BasicBorders;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
@@ -136,7 +135,7 @@ public class FlatBorder
|
|||||||
Paint borderColor = (outlineColor != null) ? outlineColor : getBorderColor( c );
|
Paint borderColor = (outlineColor != null) ? outlineColor : getBorderColor( c );
|
||||||
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
||||||
focusWidth, 1, focusInnerWidth, borderWidth, arc,
|
focusWidth, 1, focusInnerWidth, borderWidth, arc,
|
||||||
focusColor, borderColor, null );
|
focusColor, borderColor, null, c instanceof JScrollPane );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
@@ -195,8 +194,7 @@ public class FlatBorder
|
|||||||
protected boolean isEnabled( Component c ) {
|
protected boolean isEnabled( Component c ) {
|
||||||
if( c instanceof JScrollPane ) {
|
if( c instanceof JScrollPane ) {
|
||||||
// check whether view component is disabled
|
// check whether view component is disabled
|
||||||
JViewport viewport = ((JScrollPane)c).getViewport();
|
Component view = FlatScrollPaneUI.getView( (JScrollPane) c );
|
||||||
Component view = (viewport != null) ? viewport.getView() : null;
|
|
||||||
if( view != null && !isEnabled( view ) )
|
if( view != null && !isEnabled( view ) )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -279,7 +277,7 @@ public class FlatBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the (unscaled) arc diameter of the border.
|
* Returns the (unscaled) arc diameter of the border corners.
|
||||||
*/
|
*/
|
||||||
protected int getArc( Component c ) {
|
protected int getArc( Component c ) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Button.disabledBorderColor Color
|
* @uiDefault Button.disabledBorderColor Color
|
||||||
* @uiDefault Button.focusedBorderColor Color
|
* @uiDefault Button.focusedBorderColor Color
|
||||||
* @uiDefault Button.hoverBorderColor Color optional
|
* @uiDefault Button.hoverBorderColor Color optional
|
||||||
|
* @uiDefault Button.pressedBorderColor Color optional
|
||||||
|
*
|
||||||
|
* @uiDefault Button.selectedBorderColor Color optional
|
||||||
|
* @uiDefault Button.disabledSelectedBorderColor Color optional
|
||||||
|
* @uiDefault Button.focusedSelectedBorderColor Color optional
|
||||||
|
* @uiDefault Button.hoverSelectedBorderColor Color optional
|
||||||
|
* @uiDefault Button.pressedSelectedBorderColor Color optional
|
||||||
*
|
*
|
||||||
* @uiDefault Button.default.borderWidth int or float
|
* @uiDefault Button.default.borderWidth int or float
|
||||||
* @uiDefault Button.default.borderColor Color
|
* @uiDefault Button.default.borderColor Color
|
||||||
@@ -49,6 +56,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Button.default.endBorderColor Color optional; if set, a gradient paint is used
|
* @uiDefault Button.default.endBorderColor Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault Button.default.focusedBorderColor Color
|
* @uiDefault Button.default.focusedBorderColor Color
|
||||||
* @uiDefault Button.default.focusColor Color
|
* @uiDefault Button.default.focusColor Color
|
||||||
|
* @uiDefault Button.default.pressedBorderColor Color optional
|
||||||
* @uiDefault Button.default.hoverBorderColor Color optional
|
* @uiDefault Button.default.hoverBorderColor Color optional
|
||||||
*
|
*
|
||||||
* @uiDefault Button.toolbar.focusWidth int or float optional; default is 1.5
|
* @uiDefault Button.toolbar.focusWidth int or float optional; default is 1.5
|
||||||
@@ -65,6 +73,13 @@ public class FlatButtonBorder
|
|||||||
|
|
||||||
protected Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
|
protected Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
|
||||||
@Styleable protected Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
|
@Styleable protected Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable protected Color pressedBorderColor = UIManager.getColor( "Button.pressedBorderColor" );
|
||||||
|
|
||||||
|
/** @since 3.5 */ @Styleable protected Color selectedBorderColor = UIManager.getColor( "Button.selectedBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable protected Color disabledSelectedBorderColor = UIManager.getColor( "Button.disabledSelectedBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable protected Color focusedSelectedBorderColor = UIManager.getColor( "Button.focusedSelectedBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable protected Color hoverSelectedBorderColor = UIManager.getColor( "Button.hoverSelectedBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable protected Color pressedSelectedBorderColor = UIManager.getColor( "Button.pressedSelectedBorderColor" );
|
||||||
|
|
||||||
@Styleable(dot=true) protected float defaultBorderWidth = FlatUIUtils.getUIFloat( "Button.default.borderWidth", 1 );
|
@Styleable(dot=true) protected float defaultBorderWidth = FlatUIUtils.getUIFloat( "Button.default.borderWidth", 1 );
|
||||||
@Styleable(dot=true) protected Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
|
@Styleable(dot=true) protected Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
|
||||||
@@ -72,6 +87,7 @@ public class FlatButtonBorder
|
|||||||
@Styleable(dot=true) protected Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
@Styleable(dot=true) protected Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
||||||
@Styleable(dot=true) protected Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
@Styleable(dot=true) protected Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
||||||
@Styleable(dot=true) protected Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
@Styleable(dot=true) protected Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable(dot=true) protected Color defaultPressedBorderColor = UIManager.getColor( "Button.default.pressedBorderColor" );
|
||||||
|
|
||||||
/** @since 1.4 */ @Styleable(dot=true) protected float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
|
/** @since 1.4 */ @Styleable(dot=true) protected float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
|
||||||
/** @since 1.4 */ @Styleable(dot=true) protected Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
|
/** @since 1.4 */ @Styleable(dot=true) protected Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
|
||||||
@@ -139,12 +155,13 @@ public class FlatButtonBorder
|
|||||||
@Override
|
@Override
|
||||||
protected Paint getBorderColor( Component c ) {
|
protected Paint getBorderColor( Component c ) {
|
||||||
boolean def = FlatButtonUI.isDefaultButton( c );
|
boolean def = FlatButtonUI.isDefaultButton( c );
|
||||||
|
boolean selected = (c instanceof AbstractButton && ((AbstractButton)c).isSelected());
|
||||||
Paint color = FlatButtonUI.buttonStateColor( c,
|
Paint color = FlatButtonUI.buttonStateColor( c,
|
||||||
def ? defaultBorderColor : borderColor,
|
def ? defaultBorderColor : ((selected && selectedBorderColor != null) ? selectedBorderColor : borderColor),
|
||||||
disabledBorderColor,
|
(selected && disabledSelectedBorderColor != null) ? disabledSelectedBorderColor : disabledBorderColor,
|
||||||
def ? defaultFocusedBorderColor : focusedBorderColor,
|
def ? defaultFocusedBorderColor : ((selected && focusedSelectedBorderColor != null) ? focusedSelectedBorderColor : focusedBorderColor),
|
||||||
def ? defaultHoverBorderColor : hoverBorderColor,
|
def ? defaultHoverBorderColor : ((selected && hoverSelectedBorderColor != null) ? hoverSelectedBorderColor : hoverBorderColor),
|
||||||
null );
|
def ? defaultPressedBorderColor : ((selected && pressedSelectedBorderColor != null) ? pressedSelectedBorderColor : pressedBorderColor) );
|
||||||
|
|
||||||
// change to gradient paint if start/end colors are specified
|
// change to gradient paint if start/end colors are specified
|
||||||
Color startBg = def ? defaultBorderColor : borderColor;
|
Color startBg = def ? defaultBorderColor : borderColor;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -61,6 +62,7 @@ import com.formdev.flatlaf.icons.FlatHelpButtonIcon;
|
|||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -300,6 +302,10 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
|
case BasicHTML.propertyKey:
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( b );
|
||||||
|
break;
|
||||||
|
|
||||||
case SQUARE_SIZE:
|
case SQUARE_SIZE:
|
||||||
case MINIMUM_WIDTH:
|
case MINIMUM_WIDTH:
|
||||||
case MINIMUM_HEIGHT:
|
case MINIMUM_HEIGHT:
|
||||||
@@ -308,11 +314,11 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
case BUTTON_TYPE:
|
case BUTTON_TYPE:
|
||||||
b.revalidate();
|
b.revalidate();
|
||||||
b.repaint();
|
HiDPIUtils.repaint( b );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OUTLINE:
|
case OUTLINE:
|
||||||
b.repaint();
|
HiDPIUtils.repaint( b );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STYLE:
|
case STYLE:
|
||||||
@@ -324,7 +330,7 @@ public class FlatButtonUI
|
|||||||
} else
|
} else
|
||||||
installStyle( b );
|
installStyle( b );
|
||||||
b.revalidate();
|
b.revalidate();
|
||||||
b.repaint();
|
HiDPIUtils.repaint( b );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -583,9 +589,16 @@ public class FlatButtonUI
|
|||||||
// paint text
|
// paint text
|
||||||
if( clippedText != null && !clippedText.isEmpty() ) {
|
if( clippedText != null && !clippedText.isEmpty() ) {
|
||||||
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
|
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
|
||||||
if( view != null )
|
if( view != null ) {
|
||||||
|
// update foreground color in HTML view, which is necessary
|
||||||
|
// for selected and pressed states
|
||||||
|
// (only for enabled buttons, because UIManager.getColor("textInactiveText")
|
||||||
|
// is used for disabled components; see: javax.swing.text.GlyphView.paint())
|
||||||
|
if( b.isEnabled() )
|
||||||
|
FlatHTML.updateRendererCSSForeground( view, getForeground( b ) );
|
||||||
|
|
||||||
view.paint( g, textR ); // HTML text
|
view.paint( g, textR ); // HTML text
|
||||||
else
|
} else
|
||||||
paintText( g, b, textR, clippedText );
|
paintText( g, b, textR, clippedText );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,8 +644,6 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
|
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
|
||||||
if(foreground == null)
|
|
||||||
foreground=Color.red;
|
|
||||||
FontMetrics fm = b.getFontMetrics( b.getFont() );
|
FontMetrics fm = b.getFontMetrics( b.getFont() );
|
||||||
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
|
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
|
||||||
|
|
||||||
@@ -721,14 +732,15 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Color getForeground( JComponent c ) {
|
protected Color getForeground( JComponent c ) {
|
||||||
|
Color fg = c.getForeground();
|
||||||
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
|
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
|
||||||
|
|
||||||
// selected state
|
// selected state
|
||||||
if( ((AbstractButton)c).isSelected() ) {
|
if( ((AbstractButton)c).isSelected() ) {
|
||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
toolBarButton
|
toolBarButton
|
||||||
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : c.getForeground())
|
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : fg)
|
||||||
: selectedForeground,
|
: (isCustomForeground( fg ) ? fg : selectedForeground),
|
||||||
toolBarButton
|
toolBarButton
|
||||||
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
|
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
|
||||||
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
|
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
|
||||||
@@ -740,7 +752,7 @@ public class FlatButtonUI
|
|||||||
// toolbar button
|
// toolbar button
|
||||||
if( toolBarButton ) {
|
if( toolBarButton ) {
|
||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
c.getForeground(),
|
fg,
|
||||||
disabledText,
|
disabledText,
|
||||||
null,
|
null,
|
||||||
toolbarHoverForeground,
|
toolbarHoverForeground,
|
||||||
@@ -751,7 +763,7 @@ public class FlatButtonUI
|
|||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
getForegroundBase( c, def ),
|
getForegroundBase( c, def ),
|
||||||
disabledText,
|
disabledText,
|
||||||
isCustomForeground( c.getForeground() ) ? null : (def ? defaultFocusedForeground : focusedForeground),
|
isCustomForeground( fg ) ? null : (def ? defaultFocusedForeground : focusedForeground),
|
||||||
def ? defaultHoverForeground : hoverForeground,
|
def ? defaultHoverForeground : hoverForeground,
|
||||||
def ? defaultPressedForeground : pressedForeground );
|
def ? defaultPressedForeground : pressedForeground );
|
||||||
}
|
}
|
||||||
@@ -905,7 +917,7 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stateChanged( ChangeEvent e ) {
|
public void stateChanged( ChangeEvent e ) {
|
||||||
super.stateChanged( e );
|
HiDPIUtils.repaint( b );
|
||||||
|
|
||||||
// if button is in toolbar, repaint button groups
|
// if button is in toolbar, repaint button groups
|
||||||
AbstractButton b = (AbstractButton) e.getSource();
|
AbstractButton b = (AbstractButton) e.getSource();
|
||||||
@@ -917,5 +929,17 @@ public class FlatButtonUI
|
|||||||
((FlatToolBarUI)ui).repaintButtonGroup( b );
|
((FlatToolBarUI)ui).repaintButtonGroup( b );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusGained( FocusEvent e ) {
|
||||||
|
super.focusGained( e );
|
||||||
|
HiDPIUtils.repaint( b );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost( FocusEvent e ) {
|
||||||
|
super.focusLost( e );
|
||||||
|
HiDPIUtils.repaint( b );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandles;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
||||||
@@ -102,13 +103,23 @@ public class FlatCheckBoxMenuItemUI
|
|||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installComponents( JMenuItem menuItem ) {
|
||||||
|
super.installComponents( menuItem );
|
||||||
|
|
||||||
|
// update HTML renderer if necessary
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
|
||||||
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
return FlatHTML.createPropertyChangeListener(
|
||||||
|
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
|
||||||
|
super.createPropertyChangeListener( c ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
|||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
@@ -220,7 +221,7 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
private void repaintArrowButton() {
|
private void repaintArrowButton() {
|
||||||
if( arrowButton != null && !comboBox.isEditable() )
|
if( arrowButton != null && !comboBox.isEditable() )
|
||||||
arrowButton.repaint();
|
HiDPIUtils.repaint( arrowButton );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
comboBox.addMouseListener( hoverListener );
|
comboBox.addMouseListener( hoverListener );
|
||||||
@@ -351,15 +352,15 @@ public class FlatComboBoxUI
|
|||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
super.focusGained( e );
|
super.focusGained( e );
|
||||||
if( comboBox != null && comboBox.isEditable() )
|
if( comboBox != null )
|
||||||
comboBox.repaint();
|
HiDPIUtils.repaint( comboBox );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
super.focusLost( e );
|
super.focusLost( e );
|
||||||
if( comboBox != null && comboBox.isEditable() )
|
if( comboBox != null )
|
||||||
comboBox.repaint();
|
HiDPIUtils.repaint( comboBox );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -386,12 +387,12 @@ public class FlatComboBoxUI
|
|||||||
switch( propertyName ) {
|
switch( propertyName ) {
|
||||||
case PLACEHOLDER_TEXT:
|
case PLACEHOLDER_TEXT:
|
||||||
if( editor != null )
|
if( editor != null )
|
||||||
editor.repaint();
|
HiDPIUtils.repaint( editor );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMPONENT_ROUND_RECT:
|
case COMPONENT_ROUND_RECT:
|
||||||
case OUTLINE:
|
case OUTLINE:
|
||||||
comboBox.repaint();
|
HiDPIUtils.repaint( comboBox );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MINIMUM_WIDTH:
|
case MINIMUM_WIDTH:
|
||||||
@@ -402,7 +403,7 @@ public class FlatComboBoxUI
|
|||||||
case STYLE_CLASS:
|
case STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
comboBox.revalidate();
|
comboBox.revalidate();
|
||||||
comboBox.repaint();
|
HiDPIUtils.repaint( comboBox );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -926,7 +927,7 @@ public class FlatComboBoxUI
|
|||||||
protected void configurePopup() {
|
protected void configurePopup() {
|
||||||
super.configurePopup();
|
super.configurePopup();
|
||||||
|
|
||||||
// make opaque to avoid that background shines thru border (e.g. at 150% scaling)
|
// make opaque to avoid that background shines through border (e.g. at 150% scaling)
|
||||||
setOpaque( true );
|
setOpaque( true );
|
||||||
|
|
||||||
// set popup border
|
// set popup border
|
||||||
@@ -944,7 +945,7 @@ public class FlatComboBoxUI
|
|||||||
if( popupBackground != null )
|
if( popupBackground != null )
|
||||||
list.setBackground( popupBackground );
|
list.setBackground( popupBackground );
|
||||||
|
|
||||||
// set popup background because it may shine thru when scaled (e.g. at 150%)
|
// set popup background because it may shine through when scaled (e.g. at 150%)
|
||||||
// use non-UIResource to avoid that it is overwritten when making
|
// use non-UIResource to avoid that it is overwritten when making
|
||||||
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
|
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
|
||||||
setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) );
|
setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) );
|
||||||
@@ -1090,7 +1091,7 @@ public class FlatComboBoxUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// using synchronized to avoid problems with code that modifies combo box
|
// using synchronized to avoid problems with code that modifies combo box
|
||||||
// (model, selection, etc) not on AWT thread (which should be not done)
|
// (model, selection, etc.) not on AWT thread (which should be not done)
|
||||||
synchronized void install( Component c, int focusWidth ) {
|
synchronized void install( Component c, int focusWidth ) {
|
||||||
if( !(c instanceof JComponent) )
|
if( !(c instanceof JComponent) )
|
||||||
return;
|
return;
|
||||||
@@ -1242,7 +1243,7 @@ public class FlatComboBoxUI
|
|||||||
* Key selection manager that delegates to the default manager.
|
* Key selection manager that delegates to the default manager.
|
||||||
* Shows the popup if Space key is pressed and "typed characters" buffer is empty.
|
* 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
|
* If items contain spaces (e.g. "a b") it is still possible to select them
|
||||||
* by pressing keys a, Space and b.
|
* by pressing keys 'a', 'Space' and 'b'.
|
||||||
*/
|
*/
|
||||||
private class FlatKeySelectionManager
|
private class FlatKeySelectionManager
|
||||||
implements JComboBox.KeySelectionManager, UIResource
|
implements JComboBox.KeySelectionManager, UIResource
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ public class FlatEditorPaneUI
|
|||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle.run();
|
installStyle.run();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.LayoutManager;
|
import java.awt.LayoutManager;
|
||||||
|
import java.awt.Rectangle;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
@@ -32,6 +33,7 @@ import java.lang.reflect.Method;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.ButtonGroup;
|
import javax.swing.ButtonGroup;
|
||||||
@@ -46,6 +48,7 @@ import javax.swing.JScrollPane;
|
|||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
import javax.swing.JToolBar;
|
import javax.swing.JToolBar;
|
||||||
|
import javax.swing.Scrollable;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.filechooser.FileSystemView;
|
import javax.swing.filechooser.FileSystemView;
|
||||||
@@ -164,6 +167,7 @@ public class FlatFileChooserUI
|
|||||||
{
|
{
|
||||||
private final FlatFileView fileView = new FlatFileView();
|
private final FlatFileView fileView = new FlatFileView();
|
||||||
private FlatShortcutsPanel shortcutsPanel;
|
private FlatShortcutsPanel shortcutsPanel;
|
||||||
|
private JScrollPane shortcutsScrollPane;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatFileChooserUI( (JFileChooser) c );
|
return new FlatFileChooserUI( (JFileChooser) c );
|
||||||
@@ -183,7 +187,10 @@ public class FlatFileChooserUI
|
|||||||
FlatShortcutsPanel panel = createShortcutsPanel( fc );
|
FlatShortcutsPanel panel = createShortcutsPanel( fc );
|
||||||
if( panel.getComponentCount() > 0 ) {
|
if( panel.getComponentCount() > 0 ) {
|
||||||
shortcutsPanel = panel;
|
shortcutsPanel = panel;
|
||||||
fc.add( shortcutsPanel, BorderLayout.LINE_START );
|
shortcutsScrollPane = new JScrollPane( shortcutsPanel,
|
||||||
|
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
|
||||||
|
shortcutsScrollPane.setBorder( BorderFactory.createEmptyBorder() );
|
||||||
|
fc.add( shortcutsScrollPane, BorderLayout.LINE_START );
|
||||||
fc.addPropertyChangeListener( shortcutsPanel );
|
fc.addPropertyChangeListener( shortcutsPanel );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,6 +203,7 @@ public class FlatFileChooserUI
|
|||||||
if( shortcutsPanel != null ) {
|
if( shortcutsPanel != null ) {
|
||||||
fc.removePropertyChangeListener( shortcutsPanel );
|
fc.removePropertyChangeListener( shortcutsPanel );
|
||||||
shortcutsPanel = null;
|
shortcutsPanel = null;
|
||||||
|
shortcutsScrollPane = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,7 +332,7 @@ public class FlatFileChooserUI
|
|||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
Dimension prefSize = super.getPreferredSize( c );
|
Dimension prefSize = super.getPreferredSize( c );
|
||||||
Dimension minSize = getMinimumSize( c );
|
Dimension minSize = getMinimumSize( c );
|
||||||
int shortcutsPanelWidth = (shortcutsPanel != null) ? shortcutsPanel.getPreferredSize().width : 0;
|
int shortcutsPanelWidth = (shortcutsScrollPane != null) ? shortcutsScrollPane.getPreferredSize().width : 0;
|
||||||
return new Dimension(
|
return new Dimension(
|
||||||
Math.max( prefSize.width, minSize.width + shortcutsPanelWidth ),
|
Math.max( prefSize.width, minSize.width + shortcutsPanelWidth ),
|
||||||
Math.max( prefSize.height, minSize.height ) );
|
Math.max( prefSize.height, minSize.height ) );
|
||||||
@@ -370,7 +378,11 @@ public class FlatFileChooserUI
|
|||||||
|
|
||||||
// get system icon
|
// get system icon
|
||||||
if( f != null ) {
|
if( f != null ) {
|
||||||
|
try {
|
||||||
icon = getFileChooser().getFileSystemView().getSystemIcon( f );
|
icon = getFileChooser().getFileSystemView().getSystemIcon( f );
|
||||||
|
} catch( NullPointerException ex ) {
|
||||||
|
// Java 21 may throw a NPE for exe files that use default Windows exe icon
|
||||||
|
}
|
||||||
|
|
||||||
if( icon != null ) {
|
if( icon != null ) {
|
||||||
if( icon instanceof ImageIcon )
|
if( icon instanceof ImageIcon )
|
||||||
@@ -397,7 +409,7 @@ public class FlatFileChooserUI
|
|||||||
/** @since 2.3 */
|
/** @since 2.3 */
|
||||||
public static class FlatShortcutsPanel
|
public static class FlatShortcutsPanel
|
||||||
extends JToolBar
|
extends JToolBar
|
||||||
implements PropertyChangeListener
|
implements PropertyChangeListener, Scrollable
|
||||||
{
|
{
|
||||||
private final JFileChooser fc;
|
private final JFileChooser fc;
|
||||||
|
|
||||||
@@ -416,6 +428,7 @@ public class FlatFileChooserUI
|
|||||||
super( JToolBar.VERTICAL );
|
super( JToolBar.VERTICAL );
|
||||||
this.fc = fc;
|
this.fc = fc;
|
||||||
setFloatable( false );
|
setFloatable( false );
|
||||||
|
putClientProperty( FlatClientProperties.STYLE, "hoverButtonGroupBackground: null" );
|
||||||
|
|
||||||
buttonSize = UIScale.scale( getUIDimension( "FileChooser.shortcuts.buttonSize", 84, 64 ) );
|
buttonSize = UIScale.scale( getUIDimension( "FileChooser.shortcuts.buttonSize", 84, 64 ) );
|
||||||
iconSize = getUIDimension( "FileChooser.shortcuts.iconSize", 32, 32 );
|
iconSize = getUIDimension( "FileChooser.shortcuts.iconSize", 32, 32 );
|
||||||
@@ -425,7 +438,7 @@ public class FlatFileChooserUI
|
|||||||
iconFunction = (Function<File, Icon>) UIManager.get( "FileChooser.shortcuts.iconFunction" );
|
iconFunction = (Function<File, Icon>) UIManager.get( "FileChooser.shortcuts.iconFunction" );
|
||||||
|
|
||||||
FileSystemView fsv = fc.getFileSystemView();
|
FileSystemView fsv = fc.getFileSystemView();
|
||||||
File[] files = getChooserShortcutPanelFiles( fsv );
|
File[] files = JavaCompatibility2.getChooserShortcutPanelFiles( fsv );
|
||||||
if( filesFunction != null )
|
if( filesFunction != null )
|
||||||
files = filesFunction.apply( files );
|
files = filesFunction.apply( files );
|
||||||
|
|
||||||
@@ -457,7 +470,7 @@ public class FlatFileChooserUI
|
|||||||
icon = new ShortcutIcon( icon, iconSize.width, iconSize.height );
|
icon = new ShortcutIcon( icon, iconSize.width, iconSize.height );
|
||||||
|
|
||||||
// create button
|
// create button
|
||||||
JToggleButton button = createButton( name, icon );
|
JToggleButton button = createButton( name, icon, file.toString() );
|
||||||
File f = file;
|
File f = file;
|
||||||
button.addActionListener( e -> {
|
button.addActionListener( e -> {
|
||||||
fc.setCurrentDirectory( f );
|
fc.setCurrentDirectory( f );
|
||||||
@@ -483,8 +496,10 @@ public class FlatFileChooserUI
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected JToggleButton createButton( String name, Icon icon ) {
|
/** @since 3.5 */
|
||||||
|
protected JToggleButton createButton( String name, Icon icon, String toolTip ) {
|
||||||
JToggleButton button = new JToggleButton( name, icon );
|
JToggleButton button = new JToggleButton( name, icon );
|
||||||
|
button.setToolTipText( toolTip );
|
||||||
button.setVerticalTextPosition( SwingConstants.BOTTOM );
|
button.setVerticalTextPosition( SwingConstants.BOTTOM );
|
||||||
button.setHorizontalTextPosition( SwingConstants.CENTER );
|
button.setHorizontalTextPosition( SwingConstants.CENTER );
|
||||||
button.setAlignmentX( Component.CENTER_ALIGNMENT );
|
button.setAlignmentX( Component.CENTER_ALIGNMENT );
|
||||||
@@ -494,32 +509,6 @@ public class FlatFileChooserUI
|
|||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected File[] getChooserShortcutPanelFiles( FileSystemView fsv ) {
|
|
||||||
try {
|
|
||||||
if( SystemInfo.isJava_12_orLater ) {
|
|
||||||
Method m = fsv.getClass().getMethod( "getChooserShortcutPanelFiles" );
|
|
||||||
File[] files = (File[]) m.invoke( fsv );
|
|
||||||
|
|
||||||
// on macOS and Linux, files consists only of the user home directory
|
|
||||||
if( files.length == 1 && files[0].equals( new File( System.getProperty( "user.home" ) ) ) )
|
|
||||||
files = new File[0];
|
|
||||||
|
|
||||||
return files;
|
|
||||||
} else if( SystemInfo.isWindows ) {
|
|
||||||
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
|
|
||||||
Method m = cls.getMethod( "get", String.class );
|
|
||||||
return (File[]) m.invoke( null, "fileChooserShortcutPanelFolders" );
|
|
||||||
}
|
|
||||||
} catch( IllegalAccessException ex ) {
|
|
||||||
// do not log because access may be denied via VM option '--illegal-access=deny'
|
|
||||||
} catch( Exception ex ) {
|
|
||||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
|
||||||
}
|
|
||||||
|
|
||||||
// fallback
|
|
||||||
return new File[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getDisplayName( FileSystemView fsv, File file ) {
|
protected String getDisplayName( FileSystemView fsv, File file ) {
|
||||||
if( displayNameFunction != null ) {
|
if( displayNameFunction != null ) {
|
||||||
String name = displayNameFunction.apply( file );
|
String name = displayNameFunction.apply( file );
|
||||||
@@ -540,6 +529,7 @@ public class FlatFileChooserUI
|
|||||||
if( doNotUseSystemIcons() )
|
if( doNotUseSystemIcons() )
|
||||||
return new FlatFileViewDirectoryIcon();
|
return new FlatFileViewDirectoryIcon();
|
||||||
|
|
||||||
|
try {
|
||||||
// Java 17+ supports getting larger system icons
|
// Java 17+ supports getting larger system icons
|
||||||
try {
|
try {
|
||||||
if( SystemInfo.isJava_17_orLater ) {
|
if( SystemInfo.isJava_17_orLater ) {
|
||||||
@@ -565,6 +555,10 @@ public class FlatFileChooserUI
|
|||||||
|
|
||||||
// get system icon in default size 16x16
|
// get system icon in default size 16x16
|
||||||
return fsv.getSystemIcon( file );
|
return fsv.getSystemIcon( file );
|
||||||
|
} catch( NullPointerException ex ) {
|
||||||
|
// Java 21 may throw a NPE for exe files that use default Windows exe icon
|
||||||
|
return new FlatFileViewDirectoryIcon();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void directoryChanged( File file ) {
|
protected void directoryChanged( File file ) {
|
||||||
@@ -583,6 +577,8 @@ public class FlatFileChooserUI
|
|||||||
buttonGroup.clearSelection();
|
buttonGroup.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface PropertyChangeListener ----
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
@@ -591,6 +587,41 @@ public class FlatFileChooserUI
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface Scrollable ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getPreferredScrollableViewportSize() {
|
||||||
|
if( getComponentCount() > 0 ) {
|
||||||
|
Insets insets = getInsets();
|
||||||
|
int height = (getComponent( 0 ).getPreferredSize().height * 5) + insets.top + insets.bottom;
|
||||||
|
return new Dimension( getPreferredSize().width, height );
|
||||||
|
}
|
||||||
|
return getPreferredSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction ) {
|
||||||
|
if( orientation == SwingConstants.VERTICAL && getComponentCount() > 0 )
|
||||||
|
return getComponent( 0 ).getPreferredSize().height;
|
||||||
|
|
||||||
|
return getScrollableBlockIncrement( visibleRect, orientation, direction ) / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction ) {
|
||||||
|
return (orientation == SwingConstants.VERTICAL) ? visibleRect.height : visibleRect.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getScrollableTracksViewportWidth() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getScrollableTracksViewportHeight() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class ShortcutIcon -------------------------------------------------
|
//---- class ShortcutIcon -------------------------------------------------
|
||||||
|
|||||||
255
flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatHTML.java
Normal file
255
flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatHTML.java
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.swing.AbstractButton;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JToolTip;
|
||||||
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
|
import javax.swing.text.AttributeSet;
|
||||||
|
import javax.swing.text.Document;
|
||||||
|
import javax.swing.text.LabelView;
|
||||||
|
import javax.swing.text.Style;
|
||||||
|
import javax.swing.text.StyleConstants;
|
||||||
|
import javax.swing.text.View;
|
||||||
|
import javax.swing.text.html.CSS;
|
||||||
|
import javax.swing.text.html.HTMLDocument;
|
||||||
|
import javax.swing.text.html.StyleSheet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public class FlatHTML
|
||||||
|
{
|
||||||
|
private FlatHTML() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds CSS rule BASE_SIZE to the style sheet of the HTML view,
|
||||||
|
* which re-calculates font sizes based on current component font size.
|
||||||
|
* This is necessary for "absolute-size" keywords (e.g. "x-large")
|
||||||
|
* for "font-size" attributes in default style sheet (see javax/swing/text/html/default.css).
|
||||||
|
* See also <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-size#values">CSS font-size</a>.
|
||||||
|
* <p>
|
||||||
|
* This method should be invoked after {@link BasicHTML#updateRenderer(JComponent, String)}.
|
||||||
|
*/
|
||||||
|
public static void updateRendererCSSFontBaseSize( JComponent c ) {
|
||||||
|
View view = (View) c.getClientProperty( BasicHTML.propertyKey );
|
||||||
|
if( view == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// dumpViews( view, 0 );
|
||||||
|
|
||||||
|
Document doc = view.getDocument();
|
||||||
|
if( !(doc instanceof HTMLDocument) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// add BASE_SIZE rule if necessary
|
||||||
|
// - if point size at index 7 is not 36, then probably HTML text contains BASE_SIZE rule
|
||||||
|
// - if point size at index 4 is equal to given font size, then it is not necessary to add BASE_SIZE rule
|
||||||
|
StyleSheet styleSheet = ((HTMLDocument)doc).getStyleSheet();
|
||||||
|
/*debug
|
||||||
|
for( int i = 1; i <= 7; i++ )
|
||||||
|
System.out.println( i+": "+ styleSheet.getPointSize( i ) );
|
||||||
|
debug*/
|
||||||
|
int fontBaseSize = c.getFont().getSize();
|
||||||
|
if( styleSheet.getPointSize( 7 ) != 36f ||
|
||||||
|
styleSheet.getPointSize( 4 ) == fontBaseSize )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check whether view uses "absolute-size" keywords (e.g. "x-large") for font-size
|
||||||
|
if( !usesAbsoluteSizeKeywordForFontSize( view ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// get HTML text from component
|
||||||
|
String text;
|
||||||
|
if( c instanceof JLabel )
|
||||||
|
text = ((JLabel)c).getText();
|
||||||
|
else if( c instanceof AbstractButton )
|
||||||
|
text = ((AbstractButton)c).getText();
|
||||||
|
else if( c instanceof JToolTip )
|
||||||
|
text = ((JToolTip)c).getTipText();
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
if( text == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
|
||||||
|
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
|
||||||
|
String openTag = "";
|
||||||
|
String closeTag = "";
|
||||||
|
|
||||||
|
String lowerText = text.toLowerCase( Locale.ENGLISH );
|
||||||
|
int headIndex;
|
||||||
|
int styleIndex;
|
||||||
|
|
||||||
|
int insertIndex;
|
||||||
|
if( (headIndex = lowerText.indexOf( "<head>" )) >= 0 ) {
|
||||||
|
// there is a <head> tag --> insert after <head> tag
|
||||||
|
insertIndex = headIndex + "<head>".length();
|
||||||
|
} else if( (styleIndex = lowerText.indexOf( "<style>" )) >= 0 ) {
|
||||||
|
// there is a <style> tag --> insert before <style> tag
|
||||||
|
insertIndex = styleIndex;
|
||||||
|
} else {
|
||||||
|
// no <head> or <style> tag --> insert <head> tag after <html> tag
|
||||||
|
insertIndex = "<html>".length();
|
||||||
|
openTag = "<head>";
|
||||||
|
closeTag = "</head>";
|
||||||
|
}
|
||||||
|
|
||||||
|
String newText = text.substring( 0, insertIndex )
|
||||||
|
+ openTag + style + closeTag
|
||||||
|
+ text.substring( insertIndex );
|
||||||
|
|
||||||
|
BasicHTML.updateRenderer( c, newText );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Set<String> absoluteSizeKeywordsSet = new HashSet<>( Arrays.asList(
|
||||||
|
"xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large" ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether view uses "absolute-size" keywords (e.g. "x-large") for font-size
|
||||||
|
* (see javax/swing/text/html/default.css).
|
||||||
|
*/
|
||||||
|
private static boolean usesAbsoluteSizeKeywordForFontSize( View view ) {
|
||||||
|
AttributeSet attributes = view.getAttributes();
|
||||||
|
if( attributes != null ) {
|
||||||
|
Object fontSize = attributes.getAttribute( CSS.Attribute.FONT_SIZE );
|
||||||
|
if( fontSize != null ) {
|
||||||
|
if( absoluteSizeKeywordsSet.contains( fontSize.toString() ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int viewCount = view.getViewCount();
|
||||||
|
for( int i = 0; i < viewCount; i++ ) {
|
||||||
|
if( usesAbsoluteSizeKeywordForFontSize( view.getView( i ) ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates foreground in style sheet of the HTML view.
|
||||||
|
* Adds "body { color: #<foreground-hex>; }"
|
||||||
|
*/
|
||||||
|
public static void updateRendererCSSForeground( View view, Color foreground ) {
|
||||||
|
Document doc = view.getDocument();
|
||||||
|
if( !(doc instanceof HTMLDocument) || foreground == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// add foreground rule if necessary
|
||||||
|
// - use tag 'body' because BasicHTML.createHTMLView() also uses this tag
|
||||||
|
// to set font and color styles to component font/color
|
||||||
|
// see: SwingUtilities2.displayPropertiesToCSS()
|
||||||
|
// - this color is not used if component is disabled;
|
||||||
|
// JTextComponent.getDisabledTextColor() is used for disabled text components;
|
||||||
|
// UIManager.getColor("textInactiveText") is used for other disabled components
|
||||||
|
// see: javax.swing.text.GlyphView.paint()
|
||||||
|
Style bodyStyle = ((HTMLDocument)doc).getStyle( "body" );
|
||||||
|
if( bodyStyle == null ) {
|
||||||
|
StyleSheet styleSheet = ((HTMLDocument)doc).getStyleSheet();
|
||||||
|
styleSheet.addRule( String.format( "body { color: #%06x; }", foreground.getRGB() & 0xffffff ) );
|
||||||
|
clearViewCaches( view );
|
||||||
|
} else if( !foreground.equals( bodyStyle.getAttribute( StyleConstants.Foreground ) ) ) {
|
||||||
|
bodyStyle.addAttribute( StyleConstants.Foreground, foreground );
|
||||||
|
clearViewCaches( view );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears cached values in view so that CSS changes take effect.
|
||||||
|
*/
|
||||||
|
private static void clearViewCaches( View view ) {
|
||||||
|
if( view instanceof LabelView )
|
||||||
|
((LabelView)view).changedUpdate( null, null, null );
|
||||||
|
|
||||||
|
int viewCount = view.getViewCount();
|
||||||
|
for( int i = 0; i < viewCount; i++ )
|
||||||
|
clearViewCaches( view.getView( i ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PropertyChangeListener createPropertyChangeListener( PropertyChangeListener superListener ) {
|
||||||
|
return e -> {
|
||||||
|
if( superListener != null )
|
||||||
|
superListener.propertyChange( e );
|
||||||
|
propertyChange( e );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes {@link #updateRendererCSSFontBaseSize(JComponent)}
|
||||||
|
* for {@link BasicHTML#propertyKey} property change events,
|
||||||
|
* which are fired when {@link BasicHTML#updateRenderer(JComponent, String)}
|
||||||
|
* updates the HTML view.
|
||||||
|
*/
|
||||||
|
public static void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
if( BasicHTML.propertyKey.equals( e.getPropertyName() ) && e.getNewValue() instanceof View )
|
||||||
|
updateRendererCSSFontBaseSize( (JComponent) e.getSource() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
public static void dumpView( JComponent c ) {
|
||||||
|
View view = (View) c.getClientProperty( BasicHTML.propertyKey );
|
||||||
|
if( view != null )
|
||||||
|
dumpViews( view, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void dumpViews( View view, int indent ) {
|
||||||
|
for( int i = 0; i < indent; i++ )
|
||||||
|
System.out.print( " " );
|
||||||
|
|
||||||
|
System.out.printf( "%s @%-8x %3d,%2d",
|
||||||
|
view.getClass().isAnonymousClass() ? view.getClass().getName() : view.getClass().getSimpleName(),
|
||||||
|
System.identityHashCode( view ),
|
||||||
|
(int) view.getPreferredSpan( View.X_AXIS ),
|
||||||
|
(int) view.getPreferredSpan( View.Y_AXIS ) );
|
||||||
|
|
||||||
|
AttributeSet attrs = view.getAttributes();
|
||||||
|
if( attrs != null ) {
|
||||||
|
Object fontSize = attrs.getAttribute( CSS.Attribute.FONT_SIZE );
|
||||||
|
System.out.printf( " %-8s", fontSize );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( view instanceof javax.swing.text.GlyphView ) {
|
||||||
|
javax.swing.text.GlyphView gview = ((javax.swing.text.GlyphView)view);
|
||||||
|
java.awt.Font font = gview.getFont();
|
||||||
|
System.out.printf( " %3d-%-3d %s %2d (@%x) #%06x '%s'",
|
||||||
|
gview.getStartOffset(), gview.getEndOffset() - 1,
|
||||||
|
font.getName(), font.getSize(), System.identityHashCode( font ),
|
||||||
|
gview.getForeground().getRGB() & 0xffffff,
|
||||||
|
gview.getText( gview.getStartOffset(), gview.getEndOffset() ) );
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
int viewCount = view.getViewCount();
|
||||||
|
for( int i = 0; i < viewCount; i++ ) {
|
||||||
|
View child = view.getView( i );
|
||||||
|
dumpViews( child, indent + 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug*/
|
||||||
|
}
|
||||||
@@ -22,11 +22,7 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
@@ -64,6 +60,9 @@ public class FlatLabelUI
|
|||||||
{
|
{
|
||||||
@Styleable protected Color disabledForeground;
|
@Styleable protected Color disabledForeground;
|
||||||
|
|
||||||
|
// only used via styling (not in UI defaults)
|
||||||
|
/** @since 3.5 */ @Styleable protected int arc = -1;
|
||||||
|
|
||||||
private final boolean shared;
|
private final boolean shared;
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
@@ -110,16 +109,13 @@ public class FlatLabelUI
|
|||||||
super.installComponents( c );
|
super.installComponents( c );
|
||||||
|
|
||||||
// update HTML renderer if necessary
|
// update HTML renderer if necessary
|
||||||
updateHTMLRenderer( c, c.getText(), false );
|
FlatHTML.updateRendererCSSFontBaseSize( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
String name = e.getPropertyName();
|
String name = e.getPropertyName();
|
||||||
if( name == "text" || name == "font" || name == "foreground" ) {
|
if( name.equals( FlatClientProperties.STYLE ) || name.equals( FlatClientProperties.STYLE_CLASS ) ) {
|
||||||
JLabel label = (JLabel) e.getSource();
|
|
||||||
updateHTMLRenderer( label, label.getText(), true );
|
|
||||||
} else if( name.equals( FlatClientProperties.STYLE ) || name.equals( FlatClientProperties.STYLE_CLASS ) ) {
|
|
||||||
JLabel label = (JLabel) e.getSource();
|
JLabel label = (JLabel) e.getSource();
|
||||||
if( shared && FlatStylingSupport.hasStyleProperty( label ) ) {
|
if( shared && FlatStylingSupport.hasStyleProperty( label ) ) {
|
||||||
// unshare component UI if necessary
|
// unshare component UI if necessary
|
||||||
@@ -128,9 +124,11 @@ public class FlatLabelUI
|
|||||||
} else
|
} else
|
||||||
installStyle( label );
|
installStyle( label );
|
||||||
label.revalidate();
|
label.revalidate();
|
||||||
label.repaint();
|
HiDPIUtils.repaint( label );
|
||||||
} else
|
}
|
||||||
|
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
|
FlatHTML.propertyChange( e );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@@ -165,83 +163,10 @@ public class FlatLabelUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Checks whether text contains HTML tags that use "absolute-size" keywords
|
public void update( Graphics g, JComponent c ) {
|
||||||
* (e.g. "x-large") for font-size in default style sheet
|
FlatPanelUI.fillRoundedBackground( g, c, arc );
|
||||||
* (see javax/swing/text/html/default.css).
|
paint( g, c );
|
||||||
* If yes, adds a special CSS rule (BASE_SIZE) to the HTML text, which
|
|
||||||
* re-calculates font sizes based on current component font size.
|
|
||||||
*/
|
|
||||||
static void updateHTMLRenderer( JComponent c, String text, boolean always ) {
|
|
||||||
if( BasicHTML.isHTMLString( text ) &&
|
|
||||||
c.getClientProperty( "html.disable" ) != Boolean.TRUE &&
|
|
||||||
needsFontBaseSize( text ) )
|
|
||||||
{
|
|
||||||
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
|
|
||||||
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
|
|
||||||
|
|
||||||
String lowerText = text.toLowerCase( Locale.ENGLISH );
|
|
||||||
int headIndex;
|
|
||||||
int styleIndex;
|
|
||||||
|
|
||||||
int insertIndex;
|
|
||||||
if( (headIndex = lowerText.indexOf( "<head>" )) >= 0 ) {
|
|
||||||
// there is a <head> tag --> insert after <head> tag
|
|
||||||
insertIndex = headIndex + "<head>".length();
|
|
||||||
} else if( (styleIndex = lowerText.indexOf( "<style>" )) >= 0 ) {
|
|
||||||
// there is a <style> tag --> insert before <style> tag
|
|
||||||
insertIndex = styleIndex;
|
|
||||||
} else {
|
|
||||||
// no <head> or <style> tag --> insert <head> tag after <html> tag
|
|
||||||
style = "<head>" + style + "</head>";
|
|
||||||
insertIndex = "<html>".length();
|
|
||||||
}
|
|
||||||
|
|
||||||
text = text.substring( 0, insertIndex )
|
|
||||||
+ style
|
|
||||||
+ text.substring( insertIndex );
|
|
||||||
} else if( !always )
|
|
||||||
return; // not necessary to invoke BasicHTML.updateRenderer()
|
|
||||||
|
|
||||||
BasicHTML.updateRenderer( c, text );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Set<String> tagsUseFontSizeSet;
|
|
||||||
|
|
||||||
private static boolean needsFontBaseSize( String text ) {
|
|
||||||
if( tagsUseFontSizeSet == null ) {
|
|
||||||
// tags that use font-size in javax/swing/text/html/default.css
|
|
||||||
tagsUseFontSizeSet = new HashSet<>( Arrays.asList(
|
|
||||||
"h1", "h2", "h3", "h4", "h5", "h6", "code", "kbd", "big", "small", "samp" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// search for tags in HTML text
|
|
||||||
int textLength = text.length();
|
|
||||||
for( int i = 6; i < textLength - 1; i++ ) {
|
|
||||||
if( text.charAt( i ) == '<' ) {
|
|
||||||
switch( text.charAt( i + 1 ) ) {
|
|
||||||
// first letters of tags in tagsUseFontSizeSet
|
|
||||||
case 'b': case 'B':
|
|
||||||
case 'c': case 'C':
|
|
||||||
case 'h': case 'H':
|
|
||||||
case 'k': case 'K':
|
|
||||||
case 's': case 'S':
|
|
||||||
int tagBegin = i + 1;
|
|
||||||
for( i += 2; i < textLength; i++ ) {
|
|
||||||
if( !Character.isLetterOrDigit( text.charAt( i ) ) ) {
|
|
||||||
String tag = text.substring( tagBegin, i ).toLowerCase( Locale.ENGLISH );
|
|
||||||
if( tagsUseFontSizeSet.contains( tag ) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Graphics createGraphicsHTMLTextYCorrection( Graphics g, JComponent c ) {
|
static Graphics createGraphicsHTMLTextYCorrection( Graphics g, JComponent c ) {
|
||||||
|
|||||||
@@ -22,13 +22,21 @@ import java.awt.Component;
|
|||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Line border for various components.
|
* Line border for various components.
|
||||||
*
|
* <p>
|
||||||
* Paints a scaled (usually 1px thick) line around the component.
|
* Paints a scaled (usually 1px thick) line around the component.
|
||||||
* The line thickness is not added to the border insets.
|
* The line thickness is not added to the border insets.
|
||||||
* The insets should be at least have line thickness (usually 1,1,1,1).
|
* The insets should be at least have line thickness (usually 1,1,1,1).
|
||||||
|
* <p>
|
||||||
|
* For {@link javax.swing.JPanel} and {@link javax.swing.JLabel}, this border
|
||||||
|
* can be used paint rounded background (if line color is {@code null}) or
|
||||||
|
* paint rounded line border with rounded background.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -40,7 +48,7 @@ public class FlatLineBorder
|
|||||||
/** @since 2 */ private final int arc;
|
/** @since 2 */ private final int arc;
|
||||||
|
|
||||||
public FlatLineBorder( Insets insets, Color lineColor ) {
|
public FlatLineBorder( Insets insets, Color lineColor ) {
|
||||||
this( insets, lineColor, 1f, 0 );
|
this( insets, lineColor, 1f, -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@@ -51,26 +59,62 @@ public class FlatLineBorder
|
|||||||
this.arc = arc;
|
this.arc = arc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.5 */
|
||||||
|
public FlatLineBorder( Insets insets, int arc ) {
|
||||||
|
this( insets, null, 0, arc );
|
||||||
|
}
|
||||||
|
|
||||||
public Color getLineColor() {
|
public Color getLineColor() {
|
||||||
return lineColor;
|
return lineColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the (unscaled) line thickness used to paint the border.
|
||||||
|
* The line thickness does not affect the border insets.
|
||||||
|
*/
|
||||||
public float getLineThickness() {
|
public float getLineThickness() {
|
||||||
return lineThickness;
|
return lineThickness;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/**
|
||||||
|
* Returns the (unscaled) arc diameter of the border corners.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
public int getArc() {
|
public int getArc() {
|
||||||
return arc;
|
return arc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
if( c instanceof JComponent && ((JComponent)c).getClientProperty( FlatPopupFactory.KEY_POPUP_USES_NATIVE_BORDER ) != null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Color lineColor = getLineColor();
|
||||||
|
float lineThickness = getLineThickness();
|
||||||
|
if( lineColor == null || lineThickness <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
int arc = getArc();
|
||||||
|
if( arc < 0 ) {
|
||||||
|
// get arc from label or panel
|
||||||
|
ComponentUI ui = (c instanceof JLabel)
|
||||||
|
? ((JLabel)c).getUI()
|
||||||
|
: (c instanceof JPanel ? ((JPanel)c).getUI() : null);
|
||||||
|
if( ui instanceof FlatLabelUI )
|
||||||
|
arc = ((FlatLabelUI)ui).arc;
|
||||||
|
else if( ui instanceof FlatPanelUI )
|
||||||
|
arc = ((FlatPanelUI)ui).arc;
|
||||||
|
|
||||||
|
if( arc < 0 )
|
||||||
|
arc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
try {
|
try {
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
||||||
0, 0, 0, scale( getLineThickness() ), scale( getArc() ), null, getLineColor(), null );
|
0, 0, 0, scale( lineThickness ), scale( arc ), null, lineColor, null );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -182,7 +183,7 @@ public class FlatListUI
|
|||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
list.revalidate();
|
list.revalidate();
|
||||||
list.repaint();
|
HiDPIUtils.repaint( list );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -205,7 +206,7 @@ public class FlatListUI
|
|||||||
Rectangle r = getCellBounds( list, firstIndex, lastIndex );
|
Rectangle r = getCellBounds( list, firstIndex, lastIndex );
|
||||||
if( r != null ) {
|
if( r != null ) {
|
||||||
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
|
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
|
||||||
list.repaint( r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
|
HiDPIUtils.repaint( list, r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -324,8 +325,7 @@ public class FlatListUI
|
|||||||
(rendererComponent instanceof DefaultListCellRenderer ||
|
(rendererComponent instanceof DefaultListCellRenderer ||
|
||||||
rendererComponent instanceof BasicComboBoxRenderer) &&
|
rendererComponent instanceof BasicComboBoxRenderer) &&
|
||||||
(selectionArc > 0 ||
|
(selectionArc > 0 ||
|
||||||
(selectionInsets != null &&
|
(selectionInsets != null && !FlatUIUtils.isInsetsEmpty( selectionInsets ))) )
|
||||||
(selectionInsets.top != 0 || selectionInsets.left != 0 || selectionInsets.bottom != 0 || selectionInsets.right != 0))) )
|
|
||||||
{
|
{
|
||||||
// Because selection painting is done in the cell renderer, it would be
|
// Because selection painting is done in the cell renderer, it would be
|
||||||
// necessary to require a FlatLaf specific renderer to implement rounded selection.
|
// necessary to require a FlatLaf specific renderer to implement rounded selection.
|
||||||
@@ -374,7 +374,15 @@ public class FlatListUI
|
|||||||
rendererPane.paintComponent( g, rendererComponent, list, cx, rowBounds.y, cw, rowBounds.height, true );
|
rendererPane.paintComponent( g, rendererComponent, list, cx, rowBounds.y, cw, rowBounds.height, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 3 */
|
/**
|
||||||
|
* Paints (rounded) cell selection.
|
||||||
|
* Supports {@link #selectionArc} and {@link #selectionInsets}.
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> This method is only invoked if either selection arc
|
||||||
|
* is greater than zero or if selection insets are not empty.
|
||||||
|
*
|
||||||
|
* @since 3
|
||||||
|
*/
|
||||||
protected void paintCellSelection( Graphics g, int row, int x, int y, int width, int height ) {
|
protected void paintCellSelection( Graphics g, int row, int x, int y, int width, int height ) {
|
||||||
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
|
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
|
||||||
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
|
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
|
||||||
@@ -411,7 +419,7 @@ public class FlatListUI
|
|||||||
int leftIndex = locationToIndex( list, new Point( r.x - 1, r.y ) );
|
int leftIndex = locationToIndex( list, new Point( r.x - 1, r.y ) );
|
||||||
int rightIndex = locationToIndex( list, new Point( r.x + r.width, r.y ) );
|
int rightIndex = locationToIndex( list, new Point( r.x + r.width, r.y ) );
|
||||||
|
|
||||||
// special handling for the case that last column contains less cells than the other columns
|
// special handling for the case that last column contains fewer cells than the other columns
|
||||||
boolean ltr = list.getComponentOrientation().isLeftToRight();
|
boolean ltr = list.getComponentOrientation().isLeftToRight();
|
||||||
if( !ltr && leftIndex >= 0 && leftIndex != row && leftIndex == locationToIndex( list, new Point( r.x - 1, r.y - 1 ) ) )
|
if( !ltr && leftIndex >= 0 && leftIndex != row && leftIndex == locationToIndex( list, new Point( r.x - 1, r.y - 1 ) ) )
|
||||||
leftIndex = -1;
|
leftIndex = -1;
|
||||||
@@ -440,7 +448,8 @@ public class FlatListUI
|
|||||||
* Paints a cell selection at the given coordinates.
|
* Paints a cell selection at the given coordinates.
|
||||||
* The selection color must be set on the graphics context.
|
* The selection color must be set on the graphics context.
|
||||||
* <p>
|
* <p>
|
||||||
* This method is intended for use in custom cell renderers.
|
* This method is intended for use in custom cell renderers
|
||||||
|
* to support {@link #selectionArc} and {@link #selectionInsets}.
|
||||||
*
|
*
|
||||||
* @since 3
|
* @since 3
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import java.awt.event.ActionEvent;
|
|||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.ActionMap;
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
@@ -39,7 +38,6 @@ import javax.swing.MenuElement;
|
|||||||
import javax.swing.MenuSelectionManager;
|
import javax.swing.MenuSelectionManager;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ActionMapUIResource;
|
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicMenuBarUI;
|
import javax.swing.plaf.basic.BasicMenuBarUI;
|
||||||
@@ -144,12 +142,10 @@ public class FlatMenuBarUI
|
|||||||
protected void installKeyboardActions() {
|
protected void installKeyboardActions() {
|
||||||
super.installKeyboardActions();
|
super.installKeyboardActions();
|
||||||
|
|
||||||
|
// get shared action map, used for all menu bars
|
||||||
ActionMap map = SwingUtilities.getUIActionMap( menuBar );
|
ActionMap map = SwingUtilities.getUIActionMap( menuBar );
|
||||||
if( map == null ) {
|
if( map != null && !(map.get( "takeFocus" ) instanceof TakeFocusAction) )
|
||||||
map = new ActionMapUIResource();
|
map.put( "takeFocus", new TakeFocusAction( "takeFocus" ) );
|
||||||
SwingUtilities.replaceUIActionMap( menuBar, map );
|
|
||||||
}
|
|
||||||
map.put( "takeFocus", new TakeFocus() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@@ -365,16 +361,20 @@ public class FlatMenuBarUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class TakeFocus ----------------------------------------------------
|
//---- class TakeFocusAction ----------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activates the menu bar and shows mnemonics.
|
* Activates the menu bar and shows mnemonics.
|
||||||
* On Windows, the popup of the first menu is not shown.
|
* On Windows, the popup of the first menu is not shown.
|
||||||
* On other platforms, the popup of the first menu is shown.
|
* On other platforms, the popup of the first menu is shown.
|
||||||
*/
|
*/
|
||||||
private static class TakeFocus
|
private static class TakeFocusAction
|
||||||
extends AbstractAction
|
extends FlatUIAction
|
||||||
{
|
{
|
||||||
|
TakeFocusAction( String name ) {
|
||||||
|
super( name );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed( ActionEvent e ) {
|
public void actionPerformed( ActionEvent e ) {
|
||||||
JMenuBar menuBar = (JMenuBar) e.getSource();
|
JMenuBar menuBar = (JMenuBar) e.getSource();
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ import java.awt.Font;
|
|||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.GridLayout;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
import java.awt.Paint;
|
import java.awt.Paint;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.InputEvent;
|
import java.awt.event.InputEvent;
|
||||||
@@ -35,6 +37,7 @@ import java.util.Map;
|
|||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
@@ -222,7 +225,7 @@ public class FlatMenuItemRenderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// arrow size
|
// arrow size
|
||||||
if( !isTopLevelMenu && arrowIcon != null ) {
|
if( arrowIcon != null && (!isTopLevelMenu || isInVerticalMenuBar( menuItem )) ) {
|
||||||
// gap between text and arrow
|
// gap between text and arrow
|
||||||
if( accelText == null )
|
if( accelText == null )
|
||||||
width += scale( textNoAcceleratorGap );
|
width += scale( textNoAcceleratorGap );
|
||||||
@@ -254,7 +257,8 @@ public class FlatMenuItemRenderer
|
|||||||
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
|
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
|
||||||
|
|
||||||
// layout arrow
|
// layout arrow
|
||||||
if( !isTopLevelMenu && arrowIcon != null ) {
|
boolean showArrowIcon = (arrowIcon != null && (!isTopLevelMenu || isInVerticalMenuBar( menuItem )));
|
||||||
|
if( showArrowIcon ) {
|
||||||
arrowRect.width = arrowIcon.getIconWidth();
|
arrowRect.width = arrowIcon.getIconWidth();
|
||||||
arrowRect.height = arrowIcon.getIconHeight();
|
arrowRect.height = arrowIcon.getIconHeight();
|
||||||
} else
|
} else
|
||||||
@@ -288,7 +292,7 @@ public class FlatMenuItemRenderer
|
|||||||
int accelArrowWidth = accelRect.width + arrowRect.width;
|
int accelArrowWidth = accelRect.width + arrowRect.width;
|
||||||
if( accelText != null )
|
if( accelText != null )
|
||||||
accelArrowWidth += scale( !isTopLevelMenu ? textAcceleratorGap : menuItem.getIconTextGap() );
|
accelArrowWidth += scale( !isTopLevelMenu ? textAcceleratorGap : menuItem.getIconTextGap() );
|
||||||
if( !isTopLevelMenu && arrowIcon != null ) {
|
if( showArrowIcon ) {
|
||||||
if( accelText == null )
|
if( accelText == null )
|
||||||
accelArrowWidth += scale( textNoAcceleratorGap );
|
accelArrowWidth += scale( textNoAcceleratorGap );
|
||||||
accelArrowWidth += scale( acceleratorArrowGap );
|
accelArrowWidth += scale( acceleratorArrowGap );
|
||||||
@@ -355,7 +359,7 @@ debug*/
|
|||||||
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground );
|
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground );
|
||||||
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
|
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
|
||||||
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
|
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
|
||||||
if( !isTopLevelMenu( menuItem ) )
|
if( arrowIcon != null && (!isTopLevelMenu( menuItem ) || isInVerticalMenuBar( menuItem )) )
|
||||||
paintArrowIcon( g, arrowRect, arrowIcon );
|
paintArrowIcon( g, arrowRect, arrowIcon );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,6 +524,15 @@ debug*/
|
|||||||
return menuItem instanceof JMenu && ((JMenu)menuItem).isTopLevelMenu();
|
return menuItem instanceof JMenu && ((JMenu)menuItem).isTopLevelMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.5 */
|
||||||
|
public static boolean isInVerticalMenuBar( JMenuItem menuItem ) {
|
||||||
|
if( !(menuItem instanceof JMenu) || !(menuItem.getParent() instanceof JMenuBar) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
LayoutManager layout = menuItem.getParent().getLayout();
|
||||||
|
return layout instanceof GridLayout && ((GridLayout)layout).getRows() != 1;
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean isUnderlineSelection() {
|
protected boolean isUnderlineSelection() {
|
||||||
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,13 +103,23 @@ public class FlatMenuItemUI
|
|||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installComponents( JMenuItem menuItem ) {
|
||||||
|
super.installComponents( menuItem );
|
||||||
|
|
||||||
|
// update HTML renderer if necessary
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
|
||||||
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
return FlatHTML.createPropertyChangeListener(
|
||||||
|
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
|
||||||
|
super.createPropertyChangeListener( c ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
@@ -46,6 +47,7 @@ import javax.swing.plaf.basic.BasicMenuUI;
|
|||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -135,6 +137,14 @@ public class FlatMenuUI
|
|||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installComponents( JMenuItem menuItem ) {
|
||||||
|
super.installComponents( menuItem );
|
||||||
|
|
||||||
|
// update HTML renderer if necessary
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
|
||||||
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
return new FlatMenuRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
}
|
}
|
||||||
@@ -158,7 +168,7 @@ public class FlatMenuUI
|
|||||||
JMenu menu = (JMenu) e.getSource();
|
JMenu menu = (JMenu) e.getSource();
|
||||||
if( menu.isTopLevelMenu() && menu.isRolloverEnabled() ) {
|
if( menu.isTopLevelMenu() && menu.isRolloverEnabled() ) {
|
||||||
menu.getModel().setRollover( rollover );
|
menu.getModel().setRollover( rollover );
|
||||||
menu.repaint();
|
HiDPIUtils.repaint( menu );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -166,7 +176,9 @@ public class FlatMenuUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
return FlatHTML.createPropertyChangeListener(
|
||||||
|
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
|
||||||
|
super.createPropertyChangeListener( c ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@@ -271,7 +283,7 @@ public class FlatMenuUI
|
|||||||
if( !isHover() )
|
if( !isHover() )
|
||||||
selectionBackground = getStyleFromMenuBarUI( ui -> ui.selectionBackground, menuBarSelectionBackground, selectionBackground );
|
selectionBackground = getStyleFromMenuBarUI( ui -> ui.selectionBackground, menuBarSelectionBackground, selectionBackground );
|
||||||
|
|
||||||
JMenuBar menuBar = (JMenuBar) menuItem.getParent();
|
Container menuBar = menuItem.getParent();
|
||||||
JRootPane rootPane = SwingUtilities.getRootPane( menuBar );
|
JRootPane rootPane = SwingUtilities.getRootPane( menuBar );
|
||||||
if( rootPane != null && rootPane.getParent() instanceof Window &&
|
if( rootPane != null && rootPane.getParent() instanceof Window &&
|
||||||
rootPane.getJMenuBar() == menuBar &&
|
rootPane.getJMenuBar() == menuBar &&
|
||||||
@@ -321,12 +333,17 @@ public class FlatMenuUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) {
|
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) {
|
||||||
MenuBarUI ui = ((JMenuBar)menuItem.getParent()).getUI();
|
Container menuItemParent = menuItem.getParent();
|
||||||
if( !(ui instanceof FlatMenuBarUI) )
|
if( menuItemParent instanceof JMenuBar ) {
|
||||||
return defaultValue;
|
MenuBarUI ui = ((JMenuBar) menuItemParent).getUI();
|
||||||
|
if( ui instanceof FlatMenuBarUI ) {
|
||||||
T value = f.apply( (FlatMenuBarUI) ui );
|
T value = f.apply( (FlatMenuBarUI) ui );
|
||||||
return (value != null) ? value : defaultValue;
|
if( value != null ) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.security.CodeSource;
|
|||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.NativeLibrary;
|
import com.formdev.flatlaf.util.NativeLibrary;
|
||||||
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,16 +37,18 @@ class FlatNativeLibrary
|
|||||||
private static boolean initialized;
|
private static boolean initialized;
|
||||||
private static NativeLibrary nativeLibrary;
|
private static NativeLibrary nativeLibrary;
|
||||||
|
|
||||||
|
private native static int getApiVersion();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads native library (if available) and returns whether loaded successfully.
|
* Loads native library (if available) and returns whether loaded successfully.
|
||||||
* Returns {@code false} if no native library is available.
|
* Returns {@code false} if no native library is available.
|
||||||
*/
|
*/
|
||||||
static synchronized boolean isLoaded() {
|
static synchronized boolean isLoaded( int apiVersion ) {
|
||||||
initialize();
|
initialize( apiVersion );
|
||||||
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
|
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void initialize() {
|
private static void initialize( int apiVersion ) {
|
||||||
if( initialized )
|
if( initialized )
|
||||||
return;
|
return;
|
||||||
initialized = true;
|
initialized = true;
|
||||||
@@ -78,9 +81,15 @@ class FlatNativeLibrary
|
|||||||
//
|
//
|
||||||
// To avoid this, flatlaf.dll is not linked to jawt.dll,
|
// To avoid this, flatlaf.dll is not linked to jawt.dll,
|
||||||
// which avoids loading jawt.dll when flatlaf.dll is loaded.
|
// which avoids loading jawt.dll when flatlaf.dll is loaded.
|
||||||
// Instead flatlaf.dll dynamically loads jawt.dll when first used,
|
// Instead, flatlaf.dll dynamically loads jawt.dll when first used,
|
||||||
// which is guaranteed after AWT initialization.
|
// which is guaranteed after AWT initialization.
|
||||||
|
|
||||||
|
} else if( SystemInfo.isMacOS_10_14_Mojave_orLater && (SystemInfo.isAARCH64 || SystemInfo.isX86_64) ) {
|
||||||
|
// macOS: requires macOS 10.14 or later (arm64 or x86_64)
|
||||||
|
|
||||||
|
classifier = SystemInfo.isAARCH64 ? "macos-arm64" : "macos-x86_64";
|
||||||
|
ext = "dylib";
|
||||||
|
|
||||||
} else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) {
|
} else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) {
|
||||||
// Linux: requires x86_64
|
// Linux: requires x86_64
|
||||||
|
|
||||||
@@ -97,7 +106,26 @@ class FlatNativeLibrary
|
|||||||
return; // no native library available for current OS or CPU architecture
|
return; // no native library available for current OS or CPU architecture
|
||||||
|
|
||||||
// load native library
|
// load native library
|
||||||
nativeLibrary = createNativeLibrary( classifier, ext );
|
NativeLibrary nativeLibrary = createNativeLibrary( classifier, ext );
|
||||||
|
if( !nativeLibrary.isLoaded() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check API version (and check whether library works)
|
||||||
|
try {
|
||||||
|
int actualApiVersion = getApiVersion();
|
||||||
|
if( actualApiVersion != apiVersion ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Wrong API version in native library (expected "
|
||||||
|
+ apiVersion + ", actual " + actualApiVersion + "). Ignoring native library.", null );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
// could be a UnsatisfiedLinkError in case that loading native library
|
||||||
|
// from temp directory was blocked by some OS security mechanism
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to get API version of native library. Ignoring native library.", ex );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatNativeLibrary.nativeLibrary = nativeLibrary;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static NativeLibrary createNativeLibrary( String classifier, String ext ) {
|
private static NativeLibrary createNativeLibrary( String classifier, String ext ) {
|
||||||
@@ -111,13 +139,32 @@ class FlatNativeLibrary
|
|||||||
if( library.isLoaded() )
|
if( library.isLoaded() )
|
||||||
return library;
|
return library;
|
||||||
|
|
||||||
LoggingFacade.INSTANCE.logSevere( "Did not find library " + libraryName + " in java.library.path, using extracted library instead", null );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Native library '" + System.mapLibraryName( libraryName )
|
||||||
|
+ "' not found in java.library.path '" + System.getProperty( "java.library.path" )
|
||||||
|
+ "'. Using extracted native library instead.", null );
|
||||||
} else {
|
} else {
|
||||||
|
// try standard library naming scheme
|
||||||
|
// (same as in flatlaf.jar in package 'com/formdev/flatlaf/natives')
|
||||||
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
|
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
|
||||||
if( libraryFile.exists() )
|
if( libraryFile.exists() )
|
||||||
return new NativeLibrary( libraryFile, true );
|
return new NativeLibrary( libraryFile, true );
|
||||||
|
|
||||||
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
|
// try Maven naming scheme
|
||||||
|
// (see https://www.formdev.com/flatlaf/native-libraries/)
|
||||||
|
String libraryName2 = null;
|
||||||
|
File jarFile = getJarFile();
|
||||||
|
if( jarFile != null ) {
|
||||||
|
libraryName2 = buildLibraryName( jarFile, classifier, ext );
|
||||||
|
File libraryFile2 = new File( libraryPath, libraryName2 );
|
||||||
|
if( libraryFile2.exists() )
|
||||||
|
return new NativeLibrary( libraryFile2, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Native library '"
|
||||||
|
+ libraryFile.getName()
|
||||||
|
+ (libraryName2 != null ? ("' or '" + libraryName2) : "")
|
||||||
|
+ "' not found in '" + libraryFile.getParentFile().getAbsolutePath()
|
||||||
|
+ "'. Using extracted native library instead.", null );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +192,51 @@ class FlatNativeLibrary
|
|||||||
* flatlaf-3.1-linux-x86_64.so
|
* flatlaf-3.1-linux-x86_64.so
|
||||||
*/
|
*/
|
||||||
private static File findLibraryBesideJar( String classifier, String ext ) {
|
private static File findLibraryBesideJar( String classifier, String ext ) {
|
||||||
|
// get location of FlatLaf jar (or fat/uber application jar)
|
||||||
|
File jarFile = getJarFile();
|
||||||
|
if( jarFile == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// build library file
|
||||||
|
String libraryName = buildLibraryName( jarFile, classifier, ext );
|
||||||
|
File jarDir = jarFile.getParentFile();
|
||||||
|
|
||||||
|
// check whether native library exists in same directory as jar
|
||||||
|
File libraryFile = new File( jarDir, libraryName );
|
||||||
|
if( libraryFile.isFile() )
|
||||||
|
return libraryFile;
|
||||||
|
|
||||||
|
// if jar is in "lib" directory, then also check whether native library exists
|
||||||
|
// in "../bin" directory
|
||||||
|
if( jarDir.getName().equalsIgnoreCase( "lib" ) ) {
|
||||||
|
libraryFile = new File( jarDir.getParentFile(), "bin/" + libraryName );
|
||||||
|
if( libraryFile.isFile() )
|
||||||
|
return libraryFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// special case: support Gradle cache when running in development environment
|
||||||
|
// <user-home>/.gradle/caches/modules-2/files-2.1/com.formdev/flatlaf/<version>/<hash-1>/flatlaf-<version>.jar
|
||||||
|
// <user-home>/.gradle/caches/modules-2/files-2.1/com.formdev/flatlaf/<version>/<hash-2>/flatlaf-<version>-windows-x86_64.dll
|
||||||
|
String path = jarDir.getAbsolutePath().replace( '\\', '/' );
|
||||||
|
if( path.contains( "/.gradle/caches/" ) ) {
|
||||||
|
File versionDir = jarDir.getParentFile();
|
||||||
|
if( libraryName.contains( versionDir.getName() ) ) {
|
||||||
|
File[] dirs = versionDir.listFiles();
|
||||||
|
if( dirs != null ) {
|
||||||
|
for( File dir : dirs ) {
|
||||||
|
libraryFile = new File( dir, libraryName );
|
||||||
|
if( libraryFile.isFile() )
|
||||||
|
return libraryFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// native library not found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File getJarFile() {
|
||||||
try {
|
try {
|
||||||
// get location of FlatLaf jar
|
// get location of FlatLaf jar
|
||||||
CodeSource codeSource = FlatNativeLibrary.class.getProtectionDomain().getCodeSource();
|
CodeSource codeSource = FlatNativeLibrary.class.getProtectionDomain().getCodeSource();
|
||||||
@@ -162,31 +254,23 @@ class FlatNativeLibrary
|
|||||||
if( !jarFile.isFile() )
|
if( !jarFile.isFile() )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// build library file
|
return jarFile;
|
||||||
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 ) {
|
} catch( Exception ex ) {
|
||||||
LoggingFacade.INSTANCE.logSevere( ex.getMessage(), ex );
|
LoggingFacade.INSTANCE.logSevere( ex.getMessage(), ex );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
private static String buildLibraryName( File jarFile, String classifier, String ext ) {
|
||||||
|
String jarName = jarFile.getName();
|
||||||
|
String jarBasename = jarName.substring( 0, jarName.lastIndexOf( '.' ) );
|
||||||
|
|
||||||
|
// remove classifier "no-natives" (if used)
|
||||||
|
jarBasename = StringUtils.removeTrailing( jarBasename, "-no-natives" );
|
||||||
|
|
||||||
|
return jarBasename
|
||||||
|
+ (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf")
|
||||||
|
+ '-' + classifier + '.' + ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void loadJAWT() {
|
private static void loadJAWT() {
|
||||||
|
|||||||
@@ -35,8 +35,16 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
*/
|
*/
|
||||||
class FlatNativeLinuxLibrary
|
class FlatNativeLinuxLibrary
|
||||||
{
|
{
|
||||||
|
private static int API_VERSION_LINUX = 3001;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether native library is loaded/available.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: It is required to invoke this method before invoking any other
|
||||||
|
* method of this class. Otherwise, the native library may not be loaded.
|
||||||
|
*/
|
||||||
static boolean isLoaded() {
|
static boolean isLoaded() {
|
||||||
return SystemInfo.isLinux && FlatNativeLibrary.isLoaded();
|
return SystemInfo.isLinux && FlatNativeLibrary.isLoaded( API_VERSION_LINUX );
|
||||||
}
|
}
|
||||||
|
|
||||||
// direction for _NET_WM_MOVERESIZE message
|
// direction for _NET_WM_MOVERESIZE message
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Window;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native methods for macOS.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: This is private API. Do not use!
|
||||||
|
*
|
||||||
|
* <h2>Methods that use windows as parameter</h2>
|
||||||
|
*
|
||||||
|
* For all methods that accept a {@link java.awt.Window} as parameter,
|
||||||
|
* the underlying macOS window must be already created,
|
||||||
|
* otherwise the method fails. You can use following to ensure this:
|
||||||
|
* <pre>{@code
|
||||||
|
* if( !window.isDisplayable() )
|
||||||
|
* window.addNotify();
|
||||||
|
* }</pre>
|
||||||
|
* or invoke the method after packing the window. E.g.
|
||||||
|
* <pre>{@code
|
||||||
|
* window.pack();
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public class FlatNativeMacLibrary
|
||||||
|
{
|
||||||
|
private static int API_VERSION_MACOS = 2001;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether native library is loaded/available.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: It is required to invoke this method before invoking any other
|
||||||
|
* method of this class. Otherwise, the native library may not be loaded.
|
||||||
|
*/
|
||||||
|
public static boolean isLoaded() {
|
||||||
|
return SystemInfo.isMacOS && FlatNativeLibrary.isLoaded( API_VERSION_MACOS );
|
||||||
|
}
|
||||||
|
|
||||||
|
public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor );
|
||||||
|
|
||||||
|
/** @since 3.4 */
|
||||||
|
public static final int
|
||||||
|
BUTTONS_SPACING_DEFAULT = 0,
|
||||||
|
BUTTONS_SPACING_MEDIUM = 1,
|
||||||
|
BUTTONS_SPACING_LARGE = 2;
|
||||||
|
|
||||||
|
/** @since 3.4 */ public native static boolean setWindowButtonsSpacing( Window window, int buttonsSpacing );
|
||||||
|
/** @since 3.4 */ public native static Rectangle getWindowButtonsBounds( Window window );
|
||||||
|
/** @since 3.4 */ public native static boolean isWindowFullScreen( Window window );
|
||||||
|
/** @since 3.4 */ public native static boolean toggleWindowFullScreen( Window window );
|
||||||
|
}
|
||||||
@@ -17,20 +17,27 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Point;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Toolkit;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.List;
|
import java.util.function.Predicate;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.JRootPane;
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
|
import javax.swing.plaf.BorderUIResource;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
import com.formdev.flatlaf.ui.JBRCustomDecorations.JBRWindowTopBorder;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,27 +61,15 @@ public class FlatNativeWindowBorder
|
|||||||
!SystemInfo.isWinPE &&
|
!SystemInfo.isWinPE &&
|
||||||
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true );
|
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true );
|
||||||
|
|
||||||
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
|
|
||||||
private static final boolean canUseJBRCustomDecorations =
|
|
||||||
canUseWindowDecorations &&
|
|
||||||
SystemInfo.isJetBrainsJVM_11_orLater &&
|
|
||||||
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false );
|
|
||||||
|
|
||||||
private static Boolean supported;
|
private static Boolean supported;
|
||||||
private static Provider nativeProvider;
|
private static Provider nativeProvider;
|
||||||
|
|
||||||
public static boolean isSupported() {
|
public static boolean isSupported() {
|
||||||
if( canUseJBRCustomDecorations )
|
|
||||||
return JBRCustomDecorations.isSupported();
|
|
||||||
|
|
||||||
initialize();
|
initialize();
|
||||||
return supported;
|
return supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Object install( JRootPane rootPane ) {
|
static Object install( JRootPane rootPane ) {
|
||||||
if( canUseJBRCustomDecorations )
|
|
||||||
return JBRCustomDecorations.install( rootPane );
|
|
||||||
|
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -163,11 +158,6 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void uninstall( JRootPane rootPane, Object data ) {
|
static void uninstall( JRootPane rootPane, Object data ) {
|
||||||
if( canUseJBRCustomDecorations ) {
|
|
||||||
JBRCustomDecorations.uninstall( rootPane, data );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -215,9 +205,6 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasCustomDecoration( Window window ) {
|
public static boolean hasCustomDecoration( Window window ) {
|
||||||
if( canUseJBRCustomDecorations )
|
|
||||||
return JBRCustomDecorations.hasCustomDecoration( window );
|
|
||||||
|
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -225,11 +212,6 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
|
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
|
||||||
if( canUseJBRCustomDecorations ) {
|
|
||||||
JBRCustomDecorations.setHasCustomDecoration( window, hasCustomDecoration );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -237,23 +219,18 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
||||||
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
|
Predicate<Point> captionHitTestCallback, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
|
||||||
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
|
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
|
||||||
{
|
{
|
||||||
if( canUseJBRCustomDecorations ) {
|
|
||||||
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots,
|
nativeProvider.updateTitleBarInfo( window, titleBarHeight, captionHitTestCallback,
|
||||||
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
|
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean showWindow( Window window, int cmd ) {
|
static boolean showWindow( Window window, int cmd ) {
|
||||||
if( canUseJBRCustomDecorations || !isSupported() )
|
if( !isSupported() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return nativeProvider.showWindow( window, cmd );
|
return nativeProvider.showWindow( window, cmd );
|
||||||
@@ -294,7 +271,7 @@ public class FlatNativeWindowBorder
|
|||||||
{
|
{
|
||||||
boolean hasCustomDecoration( Window window );
|
boolean hasCustomDecoration( Window window );
|
||||||
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
||||||
void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
|
void updateTitleBarInfo( Window window, int titleBarHeight, Predicate<Point> captionHitTestCallback,
|
||||||
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
|
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
|
||||||
Rectangle closeButtonBounds );
|
Rectangle closeButtonBounds );
|
||||||
|
|
||||||
@@ -320,20 +297,36 @@ public class FlatNativeWindowBorder
|
|||||||
* No longer needed since Windows 11.
|
* No longer needed since Windows 11.
|
||||||
*/
|
*/
|
||||||
static class WindowTopBorder
|
static class WindowTopBorder
|
||||||
extends JBRCustomDecorations.JBRWindowTopBorder
|
extends BorderUIResource.EmptyBorderUIResource
|
||||||
{
|
{
|
||||||
private static WindowTopBorder instance;
|
private static WindowTopBorder instance;
|
||||||
|
|
||||||
static JBRWindowTopBorder getInstance() {
|
private final Color activeLightColor = new Color( 0x707070 );
|
||||||
if( canUseJBRCustomDecorations )
|
private final Color activeDarkColor = new Color( 0x2D2E2F );
|
||||||
return JBRWindowTopBorder.getInstance();
|
private final Color inactiveLightColor = new Color( 0xaaaaaa );
|
||||||
|
private final Color inactiveDarkColor = new Color( 0x494A4B );
|
||||||
|
|
||||||
|
private boolean colorizationAffectsBorders;
|
||||||
|
private Color activeColor;
|
||||||
|
|
||||||
|
static WindowTopBorder getInstance() {
|
||||||
if( instance == null )
|
if( instance == null )
|
||||||
instance = new WindowTopBorder();
|
instance = new WindowTopBorder();
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
WindowTopBorder() {
|
||||||
|
super( 1, 0, 0, 0 );
|
||||||
|
|
||||||
|
update();
|
||||||
|
installListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
colorizationAffectsBorders = isColorizationColorAffectsBorders();
|
||||||
|
activeColor = calculateActiveBorderColor();
|
||||||
|
}
|
||||||
|
|
||||||
void installListeners() {
|
void installListeners() {
|
||||||
nativeProvider.addChangeListener( e -> {
|
nativeProvider.addChangeListener( e -> {
|
||||||
update();
|
update();
|
||||||
@@ -346,19 +339,69 @@ public class FlatNativeWindowBorder
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean isColorizationColorAffectsBorders() {
|
boolean isColorizationColorAffectsBorders() {
|
||||||
return nativeProvider.isColorizationColorAffectsBorders();
|
return nativeProvider.isColorizationColorAffectsBorders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
Color getColorizationColor() {
|
Color getColorizationColor() {
|
||||||
return nativeProvider.getColorizationColor();
|
return nativeProvider.getColorizationColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
int getColorizationColorBalance() {
|
int getColorizationColorBalance() {
|
||||||
return nativeProvider.getColorizationColorBalance();
|
return nativeProvider.getColorizationColorBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Color calculateActiveBorderColor() {
|
||||||
|
if( !colorizationAffectsBorders )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Color colorizationColor = getColorizationColor();
|
||||||
|
if( colorizationColor != null ) {
|
||||||
|
int colorizationColorBalance = getColorizationColorBalance();
|
||||||
|
if( colorizationColorBalance < 0 || colorizationColorBalance > 100 )
|
||||||
|
colorizationColorBalance = 100;
|
||||||
|
|
||||||
|
if( colorizationColorBalance == 0 )
|
||||||
|
return new Color( 0xD9D9D9 );
|
||||||
|
if( colorizationColorBalance == 100 )
|
||||||
|
return colorizationColor;
|
||||||
|
|
||||||
|
float alpha = colorizationColorBalance / 100.0f;
|
||||||
|
float remainder = 1 - alpha;
|
||||||
|
int r = Math.round( colorizationColor.getRed() * alpha + 0xD9 * remainder );
|
||||||
|
int g = Math.round( colorizationColor.getGreen() * alpha + 0xD9 * remainder );
|
||||||
|
int b = Math.round( colorizationColor.getBlue() * alpha + 0xD9 * remainder );
|
||||||
|
|
||||||
|
// avoid potential IllegalArgumentException in Color constructor
|
||||||
|
r = Math.min( Math.max( r, 0 ), 255 );
|
||||||
|
g = Math.min( Math.max( g, 0 ), 255 );
|
||||||
|
b = Math.min( Math.max( b, 0 ), 255 );
|
||||||
|
|
||||||
|
return new Color( r, g, b );
|
||||||
|
}
|
||||||
|
|
||||||
|
Color activeBorderColor = (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.frame.activeBorderColor" );
|
||||||
|
return (activeBorderColor != null) ? activeBorderColor : UIManager.getColor( "MenuBar.borderColor" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
Window window = SwingUtilities.windowForComponent( c );
|
||||||
|
boolean active = window != null && window.isActive();
|
||||||
|
boolean dark = FlatLaf.isLafDark();
|
||||||
|
|
||||||
|
g.setColor( active
|
||||||
|
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
|
||||||
|
: (dark ? inactiveDarkColor : inactiveLightColor) );
|
||||||
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||||
|
g.fillRect( x, y, width, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void repaintBorder( Component c ) {
|
||||||
|
c.repaint( 0, 0, c.getWidth(), 1 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
@@ -29,10 +30,18 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
*/
|
*/
|
||||||
public class FlatNativeWindowsLibrary
|
public class FlatNativeWindowsLibrary
|
||||||
{
|
{
|
||||||
|
private static int API_VERSION_WINDOWS = 1001;
|
||||||
|
|
||||||
private static long osBuildNumber = Long.MIN_VALUE;
|
private static long osBuildNumber = Long.MIN_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether native library is loaded/available.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: It is required to invoke this method before invoking any other
|
||||||
|
* method of this class. Otherwise, the native library may not be loaded.
|
||||||
|
*/
|
||||||
public static boolean isLoaded() {
|
public static boolean isLoaded() {
|
||||||
return SystemInfo.isWindows && FlatNativeLibrary.isLoaded();
|
return SystemInfo.isWindows && FlatNativeLibrary.isLoaded( API_VERSION_WINDOWS );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,15 +102,60 @@ public class FlatNativeWindowsLibrary
|
|||||||
public native static boolean setWindowCornerPreference( long hwnd, int cornerPreference );
|
public native static boolean setWindowCornerPreference( long hwnd, int cornerPreference );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the color of the window border.
|
* DWMWINDOWATTRIBUTE
|
||||||
* The red/green/blue values must be in range {@code 0 - 255}.
|
* see https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
|
||||||
* 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.
|
* @since 3.3
|
||||||
* <p>
|
*/
|
||||||
* Invokes Win32 API method {@code DwmSetWindowAttribute(DWMWA_BORDER_COLOR)}.
|
public static final int
|
||||||
|
DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
|
||||||
|
DWMWA_BORDER_COLOR = 34,
|
||||||
|
DWMWA_CAPTION_COLOR = 35,
|
||||||
|
DWMWA_TEXT_COLOR = 36;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code BOOL} attribute value.
|
||||||
|
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public native static boolean dwmSetWindowAttributeBOOL( long hwnd, int attribute, boolean value );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code DWORD} attribute value.
|
||||||
|
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public native static boolean dwmSetWindowAttributeDWORD( long hwnd, int attribute, int value );
|
||||||
|
|
||||||
|
/** @since 3.3 */
|
||||||
|
public static final int
|
||||||
|
// use this constant to reset any window part colors to the system default behavior
|
||||||
|
DWMWA_COLOR_DEFAULT = 0xFFFFFFFF,
|
||||||
|
// use this constant to specify that a window part should not be rendered
|
||||||
|
DWMWA_COLOR_NONE = 0xFFFFFFFE;
|
||||||
|
|
||||||
|
/** @since 3.3 */
|
||||||
|
public static final Color COLOR_NONE = new Color( 0, true );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code COLORREF} attribute value.
|
||||||
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
|
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
|
||||||
* <p>
|
* <p>
|
||||||
* Supported since Windows 11 Build 22000.
|
* Supported since Windows 11 Build 22000.
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
*/
|
*/
|
||||||
public native static boolean setWindowBorderColor( long hwnd, int red, int green, int blue );
|
public static boolean dwmSetWindowAttributeCOLORREF( long hwnd, int attribute, Color color ) {
|
||||||
|
// convert color to Windows RGB value
|
||||||
|
int rgb = (color == COLOR_NONE)
|
||||||
|
? DWMWA_COLOR_NONE
|
||||||
|
: (color != null
|
||||||
|
? (color.getRed() | (color.getGreen() << 8) | (color.getBlue() << 16))
|
||||||
|
: DWMWA_COLOR_DEFAULT);
|
||||||
|
|
||||||
|
// DwmSetWindowAttribute() expects COLORREF as attribute value, which is defined as DWORD
|
||||||
|
return dwmSetWindowAttributeDWORD( hwnd, attribute, rgb );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,7 @@ import javax.swing.JPanel;
|
|||||||
import javax.swing.JRootPane;
|
import javax.swing.JRootPane;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
|
||||||
import javax.swing.plaf.basic.BasicHTML;
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.plaf.basic.BasicOptionPaneUI;
|
import javax.swing.plaf.basic.BasicOptionPaneUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
@@ -115,13 +113,6 @@ public class FlatOptionPaneUI
|
|||||||
sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true );
|
sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void installComponents() {
|
|
||||||
super.installComponents();
|
|
||||||
|
|
||||||
updateChildPanels( optionPane );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener() {
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||||
@@ -155,6 +146,13 @@ public class FlatOptionPaneUI
|
|||||||
protected Container createMessageArea() {
|
protected Container createMessageArea() {
|
||||||
Container messageArea = super.createMessageArea();
|
Container messageArea = super.createMessageArea();
|
||||||
|
|
||||||
|
// use non-UIResource OptionPane.messageAreaBorder to avoid that it is replaced when switching LaF
|
||||||
|
// and make panel non-opaque for OptionPane.background
|
||||||
|
updateAreaPanel( messageArea );
|
||||||
|
|
||||||
|
// make known sub-panels non-opaque for OptionPane.background
|
||||||
|
updateKnownChildPanels( messageArea );
|
||||||
|
|
||||||
// set icon-message gap
|
// set icon-message gap
|
||||||
if( iconMessageGap > 0 ) {
|
if( iconMessageGap > 0 ) {
|
||||||
Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" );
|
Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" );
|
||||||
@@ -169,6 +167,10 @@ public class FlatOptionPaneUI
|
|||||||
protected Container createButtonArea() {
|
protected Container createButtonArea() {
|
||||||
Container buttonArea = super.createButtonArea();
|
Container buttonArea = super.createButtonArea();
|
||||||
|
|
||||||
|
// use non-UIResource OptionPane.buttonAreaBorder to avoid that it is replaced when switching LaF
|
||||||
|
// and make panel non-opaque for OptionPane.background
|
||||||
|
updateAreaPanel( buttonArea );
|
||||||
|
|
||||||
// scale button padding and subtract focusWidth
|
// scale button padding and subtract focusWidth
|
||||||
if( buttonArea.getLayout() instanceof ButtonAreaLayout ) {
|
if( buttonArea.getLayout() instanceof ButtonAreaLayout ) {
|
||||||
ButtonAreaLayout layout = (ButtonAreaLayout) buttonArea.getLayout();
|
ButtonAreaLayout layout = (ButtonAreaLayout) buttonArea.getLayout();
|
||||||
@@ -218,22 +220,33 @@ public class FlatOptionPaneUI
|
|||||||
super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
|
super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateChildPanels( Container c ) {
|
private void updateAreaPanel( Container area ) {
|
||||||
for( Component child : c.getComponents() ) {
|
if( !(area instanceof JPanel) )
|
||||||
if( child.getClass() == JPanel.class ) {
|
return;
|
||||||
JPanel panel = (JPanel)child;
|
|
||||||
|
|
||||||
// make sub-panel non-opaque for OptionPane.background
|
// use non-UIResource border to avoid that it is replaced when switching LaF
|
||||||
|
// and make panel non-opaque for OptionPane.background
|
||||||
|
JPanel panel = (JPanel) area;
|
||||||
|
panel.setBorder( FlatUIUtils.nonUIResource( panel.getBorder() ) );
|
||||||
panel.setOpaque( false );
|
panel.setOpaque( false );
|
||||||
|
}
|
||||||
|
|
||||||
// use non-UIResource borders to avoid that they are replaced when switching LaF
|
private void updateKnownChildPanels( Container c ) {
|
||||||
Border border = panel.getBorder();
|
for( Component child : c.getComponents() ) {
|
||||||
if( border instanceof UIResource )
|
if( child instanceof JPanel && child.getName() != null ) {
|
||||||
panel.setBorder( FlatUIUtils.nonUIResource( border ) );
|
switch( child.getName() ) {
|
||||||
|
case "OptionPane.realBody":
|
||||||
|
case "OptionPane.body":
|
||||||
|
case "OptionPane.separator":
|
||||||
|
case "OptionPane.break":
|
||||||
|
// make known sub-panels non-opaque for OptionPane.background
|
||||||
|
((JPanel)child).setOpaque( false );
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( child instanceof Container )
|
if( child instanceof Container )
|
||||||
updateChildPanels( (Container) child );
|
updateKnownChildPanels( (Container) child );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
@@ -23,11 +24,14 @@ import java.beans.PropertyChangeListener;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicPanelUI;
|
import javax.swing.plaf.basic.BasicPanelUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -69,6 +73,8 @@ public class FlatPanelUI
|
|||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
c.addPropertyChangeListener( this );
|
c.addPropertyChangeListener( this );
|
||||||
|
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
|
||||||
|
FullWindowContentSupport.registerPlaceholder( c );
|
||||||
|
|
||||||
installStyle( (JPanel) c );
|
installStyle( (JPanel) c );
|
||||||
}
|
}
|
||||||
@@ -78,10 +84,20 @@ public class FlatPanelUI
|
|||||||
super.uninstallUI( c );
|
super.uninstallUI( c );
|
||||||
|
|
||||||
c.removePropertyChangeListener( this );
|
c.removePropertyChangeListener( this );
|
||||||
|
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
|
||||||
|
FullWindowContentSupport.unregisterPlaceholder( c );
|
||||||
|
|
||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installDefaults( JPanel p ) {
|
||||||
|
super.installDefaults( p );
|
||||||
|
|
||||||
|
if( p.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
|
||||||
|
LookAndFeel.installProperty( p, "opaque", false );
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 2.0.1 */
|
/** @since 2.0.1 */
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
@@ -96,7 +112,18 @@ public class FlatPanelUI
|
|||||||
} else
|
} else
|
||||||
installStyle( c );
|
installStyle( c );
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER:
|
||||||
|
JPanel p = (JPanel) e.getSource();
|
||||||
|
if( e.getOldValue() != null )
|
||||||
|
FullWindowContentSupport.unregisterPlaceholder( p );
|
||||||
|
if( e.getNewValue() != null )
|
||||||
|
FullWindowContentSupport.registerPlaceholder( p );
|
||||||
|
|
||||||
|
// make panel non-opaque for placeholders
|
||||||
|
LookAndFeel.installProperty( p, "opaque", e.getNewValue() == null );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,31 +162,52 @@ public class FlatPanelUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
// fill background
|
fillRoundedBackground( g, c, arc );
|
||||||
if( c.isOpaque() ) {
|
|
||||||
int width = c.getWidth();
|
|
||||||
int height = c.getHeight();
|
|
||||||
int arc = (this.arc >= 0)
|
|
||||||
? this.arc
|
|
||||||
: ((c.getBorder() instanceof FlatLineBorder)
|
|
||||||
? ((FlatLineBorder)c.getBorder()).getArc()
|
|
||||||
: 0);
|
|
||||||
|
|
||||||
// fill background with parent color to avoid garbage in rounded corners
|
|
||||||
if( arc > 0 )
|
|
||||||
FlatUIUtils.paintParentBackground( g, c );
|
|
||||||
|
|
||||||
g.setColor( c.getBackground() );
|
|
||||||
if( arc > 0 ) {
|
|
||||||
// fill rounded rectangle if having rounded corners
|
|
||||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
|
||||||
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, width, height,
|
|
||||||
0, UIScale.scale( arc ) );
|
|
||||||
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
|
||||||
} else
|
|
||||||
g.fillRect( 0, 0, width, height );
|
|
||||||
}
|
|
||||||
|
|
||||||
paint( g, c );
|
paint( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.5 */
|
||||||
|
public static void fillRoundedBackground( Graphics g, JComponent c, int arc ) {
|
||||||
|
if( arc < 0 ) {
|
||||||
|
Border border = c.getBorder();
|
||||||
|
arc = ((border instanceof FlatLineBorder)
|
||||||
|
? ((FlatLineBorder)border).getArc()
|
||||||
|
: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill background
|
||||||
|
if( c.isOpaque() ) {
|
||||||
|
if( arc > 0 ) {
|
||||||
|
// fill background with parent color to avoid garbage in rounded corners
|
||||||
|
FlatUIUtils.paintParentBackground( g, c );
|
||||||
|
} else {
|
||||||
|
g.setColor( c.getBackground() );
|
||||||
|
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill rounded rectangle if having rounded corners
|
||||||
|
if( arc > 0 ) {
|
||||||
|
g.setColor( c.getBackground() );
|
||||||
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, c.getWidth(), c.getHeight(),
|
||||||
|
0, UIScale.scale( arc ) );
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
|
Object value = c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER );
|
||||||
|
if( value != null )
|
||||||
|
return FullWindowContentSupport.getPlaceholderPreferredSize( c, (String) value );
|
||||||
|
|
||||||
|
return super.getPreferredSize( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
|
||||||
|
FullWindowContentSupport.debugPaint( g, c );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import javax.swing.text.View;
|
|||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.icons.FlatCapsLockIcon;
|
import com.formdev.flatlaf.icons.FlatCapsLockIcon;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,7 +164,7 @@ public class FlatPasswordFieldUI
|
|||||||
}
|
}
|
||||||
private void repaint( KeyEvent e ) {
|
private void repaint( KeyEvent e ) {
|
||||||
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK ) {
|
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK ) {
|
||||||
e.getComponent().repaint();
|
HiDPIUtils.repaint( e.getComponent() );
|
||||||
scrollCaretToVisible();
|
scrollCaretToVisible();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,7 +327,7 @@ public class FlatPasswordFieldUI
|
|||||||
if( visible != revealButton.isVisible() ) {
|
if( visible != revealButton.isVisible() ) {
|
||||||
revealButton.setVisible( visible );
|
revealButton.setVisible( visible );
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
|
|
||||||
if( !visible ) {
|
if( !visible ) {
|
||||||
revealButton.setSelected( false );
|
revealButton.setSelected( false );
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.AWTEvent;
|
import java.awt.AWTEvent;
|
||||||
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
@@ -35,12 +36,16 @@ import java.awt.Toolkit;
|
|||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.ComponentEvent;
|
import java.awt.event.ComponentEvent;
|
||||||
import java.awt.event.ComponentListener;
|
import java.awt.event.ComponentListener;
|
||||||
|
import java.awt.event.HierarchyEvent;
|
||||||
|
import java.awt.event.HierarchyListener;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.WindowFocusListener;
|
import java.awt.event.WindowFocusListener;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLayeredPane;
|
import javax.swing.JLayeredPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@@ -58,6 +63,7 @@ import javax.swing.border.EmptyBorder;
|
|||||||
import javax.swing.border.LineBorder;
|
import javax.swing.border.LineBorder;
|
||||||
import javax.swing.plaf.basic.BasicComboPopup;
|
import javax.swing.plaf.basic.BasicComboPopup;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -71,9 +77,13 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatPopupFactory
|
public class FlatPopupFactory
|
||||||
extends PopupFactory
|
extends PopupFactory
|
||||||
{
|
{
|
||||||
|
static final String KEY_POPUP_USES_NATIVE_BORDER = "FlatLaf.internal.FlatPopupFactory.popupUsesNativeBorder";
|
||||||
|
|
||||||
private MethodHandle java8getPopupMethod;
|
private MethodHandle java8getPopupMethod;
|
||||||
private MethodHandle java9getPopupMethod;
|
private MethodHandle java9getPopupMethod;
|
||||||
|
|
||||||
|
private final ArrayList<NonFlashingPopup> stillShownHeavyWeightPopups = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Popup getPopup( Component owner, Component contents, int x, int y )
|
public Popup getPopup( Component owner, Component contents, int x, int y )
|
||||||
throws IllegalArgumentException
|
throws IllegalArgumentException
|
||||||
@@ -86,23 +96,39 @@ public class FlatPopupFactory
|
|||||||
|
|
||||||
fixLinuxWaylandJava21focusIssue( owner );
|
fixLinuxWaylandJava21focusIssue( owner );
|
||||||
|
|
||||||
|
// reuse a heavy weight popup window, which is still shown on screen,
|
||||||
|
// to avoid flicker when popup (e.g. tooltip) is moving while mouse is moved
|
||||||
|
for( NonFlashingPopup popup : stillShownHeavyWeightPopups ) {
|
||||||
|
if( popup.delegate != null &&
|
||||||
|
popup.owner == owner &&
|
||||||
|
(popup.contents == contents ||
|
||||||
|
(popup.contents instanceof JToolTip && contents instanceof JToolTip)) )
|
||||||
|
{
|
||||||
|
stillShownHeavyWeightPopups.remove( popup );
|
||||||
|
return reuseStillShownHeavyWeightPopups( popup, contents, x, y );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
|
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
|
||||||
|
|
||||||
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing )
|
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing )
|
||||||
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
|
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
|
||||||
|
|
||||||
// macOS and Linux adds drop shadow to heavy weight popups
|
// macOS and Linux adds drop shadow to heavy weight popups
|
||||||
if( SystemInfo.isMacOS || SystemInfo.isLinux )
|
if( SystemInfo.isMacOS || SystemInfo.isLinux ) {
|
||||||
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
|
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), owner, contents );
|
||||||
|
if( popup.popupWindow != null && isMacOSBorderSupported() )
|
||||||
|
setupRoundedBorder( popup.popupWindow, owner, contents );
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
|
|
||||||
// Windows 11 with FlatLaf native library can use rounded corners and shows drop shadow for heavy weight popups
|
// Windows 11 with FlatLaf native library can use rounded corners and shows drop shadow for heavy weight popups
|
||||||
int borderCornerRadius;
|
|
||||||
if( isWindows11BorderSupported() &&
|
if( isWindows11BorderSupported() &&
|
||||||
(borderCornerRadius = getBorderCornerRadius( owner, contents )) > 0 )
|
getBorderCornerRadius( owner, contents ) > 0 )
|
||||||
{
|
{
|
||||||
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
|
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), owner, contents );
|
||||||
if( popup.popupWindow != null )
|
if( popup.popupWindow != null )
|
||||||
setupWindows11Border( popup.popupWindow, contents, borderCornerRadius );
|
setupRoundedBorder( popup.popupWindow, owner, contents );
|
||||||
return popup;
|
return popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +137,10 @@ public class FlatPopupFactory
|
|||||||
forceHeavyWeight = true;
|
forceHeavyWeight = true;
|
||||||
|
|
||||||
// create drop shadow popup
|
// create drop shadow popup
|
||||||
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
|
Popup popupForScreenOfOwner = getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight );
|
||||||
|
return owner.getGraphicsConfiguration().isTranslucencyCapable()
|
||||||
|
? new DropShadowPopup( popupForScreenOfOwner, owner, contents )
|
||||||
|
: new NonFlashingPopup( popupForScreenOfOwner, owner, contents );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -162,67 +191,6 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the given popup and, if necessary, fixes the location of a heavy weight popup window.
|
|
||||||
* <p>
|
|
||||||
* On a dual screen setup, where screens use different scale factors, it may happen
|
|
||||||
* that the window location changes when showing a heavy weight popup window.
|
|
||||||
* E.g. when opening a dialog on the secondary screen and making combobox popup visible.
|
|
||||||
* <p>
|
|
||||||
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
|
|
||||||
*/
|
|
||||||
private static void showPopupAndFixLocation( Popup popup, Window popupWindow ) {
|
|
||||||
if( popupWindow != null ) {
|
|
||||||
// remember location of heavy weight popup window
|
|
||||||
int x = popupWindow.getX();
|
|
||||||
int y = popupWindow.getY();
|
|
||||||
|
|
||||||
popup.show();
|
|
||||||
|
|
||||||
// restore popup window location if it has changed
|
|
||||||
// (probably scaled when screens use different scale factors)
|
|
||||||
if( popupWindow.getX() != x || popupWindow.getY() != y )
|
|
||||||
popupWindow.setLocation( x, y );
|
|
||||||
} else
|
|
||||||
popup.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
|
|
||||||
Object value = getOption( owner, contents, clientKey, uiKey );
|
|
||||||
return (value instanceof Boolean) ? (Boolean) value : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getBorderCornerRadius( Component owner, Component contents ) {
|
|
||||||
String uiKey =
|
|
||||||
(contents instanceof BasicComboPopup) ? "ComboBox.borderCornerRadius" :
|
|
||||||
(contents instanceof JPopupMenu) ? "PopupMenu.borderCornerRadius" :
|
|
||||||
(contents instanceof JToolTip) ? "ToolTip.borderCornerRadius" :
|
|
||||||
"Popup.borderCornerRadius";
|
|
||||||
|
|
||||||
Object value = getOption( owner, contents, FlatClientProperties.POPUP_BORDER_CORNER_RADIUS, uiKey );
|
|
||||||
return (value instanceof Integer) ? (Integer) value : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get option from:
|
|
||||||
* <ol>
|
|
||||||
* <li>client property {@code clientKey} of {@code owner}
|
|
||||||
* <li>client property {@code clientKey} of {@code contents}
|
|
||||||
* <li>UI property {@code uiKey}
|
|
||||||
* </ol>
|
|
||||||
*/
|
|
||||||
private Object getOption( Component owner, Component contents, String clientKey, String uiKey ) {
|
|
||||||
for( Component c : new Component[] { owner, contents } ) {
|
|
||||||
if( c instanceof JComponent ) {
|
|
||||||
Object value = ((JComponent)c).getClientProperty( clientKey );
|
|
||||||
if( value != null )
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return UIManager.get( uiKey );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* There is no API in Java 8 to force creation of heavy weight popups,
|
* There is no API in Java 8 to force creation of heavy weight popups,
|
||||||
* but it is possible with reflection. Java 9 provides a new method.
|
* but it is possible with reflection. Java 9 provides a new method.
|
||||||
@@ -258,6 +226,51 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
|
||||||
|
Object value = getOption( owner, contents, clientKey, uiKey );
|
||||||
|
return (value instanceof Boolean) ? (Boolean) value : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get option from:
|
||||||
|
* <ol>
|
||||||
|
* <li>client property {@code clientKey} of {@code owner}
|
||||||
|
* <li>client property {@code clientKey} of {@code contents}
|
||||||
|
* <li>UI property {@code uiKey}
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
private static Object getOption( Component owner, Component contents, String clientKey, String uiKey ) {
|
||||||
|
for( Component c : new Component[] { owner, contents } ) {
|
||||||
|
if( c instanceof JComponent ) {
|
||||||
|
Object value = ((JComponent)c).getClientProperty( clientKey );
|
||||||
|
if( value != null )
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UIManager.get( uiKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reuse a heavy weight popup window, which is still shown on screen,
|
||||||
|
* by updating window location and contents.
|
||||||
|
* This avoid flicker when popup (e.g. a tooltip) is moving while mouse is moved.
|
||||||
|
* E.g. overridden JComponent.getToolTipLocation(MouseEvent).
|
||||||
|
* See ToolTipManager.checkForTipChange(MouseEvent).
|
||||||
|
*/
|
||||||
|
private static NonFlashingPopup reuseStillShownHeavyWeightPopups(
|
||||||
|
NonFlashingPopup reusePopup, Component contents, int ownerX, int ownerY )
|
||||||
|
{
|
||||||
|
// clone popup because PopupFactory.getPopup() should not return old instance
|
||||||
|
NonFlashingPopup popup = reusePopup.cloneForReuse();
|
||||||
|
|
||||||
|
// update popup location, size and contents
|
||||||
|
popup.reset( contents, ownerX, ownerY );
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- tooltips -----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20).
|
* Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20).
|
||||||
* In case that the tooltip would be partly outside of the screen,
|
* In case that the tooltip would be partly outside of the screen,
|
||||||
@@ -342,16 +355,72 @@ public class FlatPopupFactory
|
|||||||
((JComponent)owner).getToolTipLocation( me ) != null;
|
((JComponent)owner).getToolTipLocation( me ) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- native rounded border ----------------------------------------------
|
||||||
|
|
||||||
private static boolean isWindows11BorderSupported() {
|
private static boolean isWindows11BorderSupported() {
|
||||||
return SystemInfo.isWindows_11_orLater && FlatNativeWindowsLibrary.isLoaded();
|
return SystemInfo.isWindows_11_orLater &&
|
||||||
|
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_ROUNDED_POPUP_BORDER, true ) &&
|
||||||
|
FlatNativeWindowsLibrary.isLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setupWindows11Border( Window popupWindow, Component contents, int borderCornerRadius ) {
|
private static boolean isMacOSBorderSupported() {
|
||||||
// make sure that the Windows 11 window is created
|
// do not use rounded border on macOS 14.4+ because it may freeze the application
|
||||||
if( !popupWindow.isDisplayable() )
|
// and crash the macOS WindowServer process (reports vary from Finder restarts to OS restarts)
|
||||||
popupWindow.addNotify();
|
// https://github.com/apache/netbeans/issues/7560#issuecomment-2226439215
|
||||||
|
// https://github.com/apache/netbeans/issues/6647#issuecomment-2070124442
|
||||||
|
boolean isMacOS_14_4_orLater = (SystemInfo.osVersion >= SystemInfo.toVersion( 14, 4, 0, 0 ));
|
||||||
|
|
||||||
// get window handle
|
return SystemInfo.isMacOS &&
|
||||||
|
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_ROUNDED_POPUP_BORDER, !isMacOS_14_4_orLater ) &&
|
||||||
|
FlatNativeMacLibrary.isLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setupRoundedBorder( Window popupWindow, Component owner, Component contents ) {
|
||||||
|
int borderCornerRadius = getBorderCornerRadius( owner, contents );
|
||||||
|
float borderWidth = getRoundedBorderWidth( owner, contents );
|
||||||
|
|
||||||
|
// get Swing border color
|
||||||
|
Color borderColor;
|
||||||
|
if( contents instanceof JComponent ) {
|
||||||
|
Border border = ((JComponent)contents).getBorder();
|
||||||
|
border = FlatUIUtils.unwrapNonUIResourceBorder( border );
|
||||||
|
|
||||||
|
// get color from border of contents (e.g. JPopupMenu or JToolTip)
|
||||||
|
if( border instanceof FlatLineBorder )
|
||||||
|
borderColor = ((FlatLineBorder)border).getLineColor();
|
||||||
|
else if( border instanceof LineBorder )
|
||||||
|
borderColor = ((LineBorder)border).getLineColor();
|
||||||
|
else if( border instanceof EmptyBorder )
|
||||||
|
borderColor = FlatNativeWindowsLibrary.COLOR_NONE; // do not paint border
|
||||||
|
else
|
||||||
|
borderColor = null; // use system default color
|
||||||
|
|
||||||
|
// avoid that FlatLineBorder paints the Swing border
|
||||||
|
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, true );
|
||||||
|
} else
|
||||||
|
borderColor = null; // use system default color
|
||||||
|
|
||||||
|
if( popupWindow.isDisplayable() ) {
|
||||||
|
// native window already created
|
||||||
|
setupRoundedBorderImpl( popupWindow, borderCornerRadius, borderWidth, borderColor );
|
||||||
|
} else {
|
||||||
|
// native window not yet created --> add listener to set native border after window creation
|
||||||
|
AtomicReference<HierarchyListener> l = new AtomicReference<>();
|
||||||
|
l.set( e -> {
|
||||||
|
if( e.getID() == HierarchyEvent.HIERARCHY_CHANGED &&
|
||||||
|
(e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0 )
|
||||||
|
{
|
||||||
|
setupRoundedBorderImpl( popupWindow, borderCornerRadius, borderWidth, borderColor );
|
||||||
|
popupWindow.removeHierarchyListener( l.get() );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
popupWindow.addHierarchyListener( l.get() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setupRoundedBorderImpl( Window popupWindow, int borderCornerRadius, float borderWidth, Color borderColor ) {
|
||||||
|
if( SystemInfo.isWindows ) {
|
||||||
|
// get native window handle
|
||||||
long hwnd = FlatNativeWindowsLibrary.getHWND( popupWindow );
|
long hwnd = FlatNativeWindowsLibrary.getHWND( popupWindow );
|
||||||
|
|
||||||
// set corner preference
|
// set corner preference
|
||||||
@@ -361,30 +430,16 @@ public class FlatPopupFactory
|
|||||||
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, cornerPreference );
|
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, cornerPreference );
|
||||||
|
|
||||||
// set border color
|
// set border color
|
||||||
int red = -1; // use system default color
|
FlatNativeWindowsLibrary.dwmSetWindowAttributeCOLORREF( hwnd, FlatNativeWindowsLibrary.DWMWA_BORDER_COLOR, borderColor );
|
||||||
int green = 0;
|
} else if( SystemInfo.isMacOS ) {
|
||||||
int blue = 0;
|
if( borderColor == null || borderColor == FlatNativeWindowsLibrary.COLOR_NONE )
|
||||||
if( contents instanceof JComponent ) {
|
borderWidth = 0;
|
||||||
Border border = ((JComponent)contents).getBorder();
|
|
||||||
border = FlatUIUtils.unwrapNonUIResourceBorder( border );
|
|
||||||
|
|
||||||
// get color from border of contents (e.g. JPopupMenu or JToolTip)
|
// set corner radius, border width and color
|
||||||
Color borderColor = null;
|
FlatNativeMacLibrary.setWindowRoundedBorder( popupWindow, borderCornerRadius,
|
||||||
if( border instanceof FlatLineBorder )
|
borderWidth, (borderColor != null) ? borderColor.getRGB() : 0 );
|
||||||
borderColor = ((FlatLineBorder)border).getLineColor();
|
|
||||||
else if( border instanceof LineBorder )
|
|
||||||
borderColor = ((LineBorder)border).getLineColor();
|
|
||||||
else if( border instanceof EmptyBorder )
|
|
||||||
red = -2; // do not paint border
|
|
||||||
|
|
||||||
if( borderColor != null ) {
|
|
||||||
red = borderColor.getRed();
|
|
||||||
green = borderColor.getGreen();
|
|
||||||
blue = borderColor.getBlue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FlatNativeWindowsLibrary.setWindowBorderColor( hwnd, red, green, blue );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void resetWindows11Border( Window popupWindow ) {
|
private static void resetWindows11Border( Window popupWindow ) {
|
||||||
// get window handle
|
// get window handle
|
||||||
@@ -396,6 +451,30 @@ public class FlatPopupFactory
|
|||||||
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, FlatNativeWindowsLibrary.DWMWCP_DONOTROUND );
|
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, FlatNativeWindowsLibrary.DWMWCP_DONOTROUND );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getBorderCornerRadius( Component owner, Component contents ) {
|
||||||
|
String uiKey =
|
||||||
|
(contents instanceof BasicComboPopup) ? "ComboBox.borderCornerRadius" :
|
||||||
|
(contents instanceof JPopupMenu) ? "PopupMenu.borderCornerRadius" :
|
||||||
|
(contents instanceof JToolTip) ? "ToolTip.borderCornerRadius" :
|
||||||
|
"Popup.borderCornerRadius";
|
||||||
|
|
||||||
|
Object value = getOption( owner, contents, FlatClientProperties.POPUP_BORDER_CORNER_RADIUS, uiKey );
|
||||||
|
return (value instanceof Integer) ? (Integer) value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getRoundedBorderWidth( Component owner, Component contents ) {
|
||||||
|
String uiKey =
|
||||||
|
(contents instanceof BasicComboPopup) ? "ComboBox.roundedBorderWidth" :
|
||||||
|
(contents instanceof JPopupMenu) ? "PopupMenu.roundedBorderWidth" :
|
||||||
|
(contents instanceof JToolTip) ? "ToolTip.roundedBorderWidth" :
|
||||||
|
"Popup.roundedBorderWidth";
|
||||||
|
|
||||||
|
Object value = getOption( owner, contents, FlatClientProperties.POPUP_ROUNDED_BORDER_WIDTH, uiKey );
|
||||||
|
return (value instanceof Number) ? ((Number)value).floatValue() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- fixes --------------------------------------------------------------
|
||||||
|
|
||||||
private static boolean overlapsHeavyWeightComponent( Component owner, Component contents, int x, int y ) {
|
private static boolean overlapsHeavyWeightComponent( Component owner, Component contents, int x, int y ) {
|
||||||
if( owner == null )
|
if( owner == null )
|
||||||
return false;
|
return false;
|
||||||
@@ -430,7 +509,7 @@ public class FlatPopupFactory
|
|||||||
* On Linux with Wayland, since Java 21, Swing adds a window focus listener to popup owner/invoker window,
|
* On Linux with Wayland, since Java 21, Swing adds a window focus listener to popup owner/invoker window,
|
||||||
* which hides the popup as soon as the owner/invoker window looses focus.
|
* which hides the popup as soon as the owner/invoker window looses focus.
|
||||||
* This works fine for light-weight popups.
|
* This works fine for light-weight popups.
|
||||||
* It also works for heavy-weight popups if the do not request focus.
|
* It also works for heavy-weight popups if they do not request focus.
|
||||||
* Because FlatLaf always uses heavy-weight popups, all popups that request focus
|
* Because FlatLaf always uses heavy-weight popups, all popups that request focus
|
||||||
* are broken since Java 21.
|
* are broken since Java 21.
|
||||||
*
|
*
|
||||||
@@ -458,20 +537,58 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the given popup and, if necessary, fixes the location of a heavy weight popup window.
|
||||||
|
* <p>
|
||||||
|
* On a dual screen setup, where screens use different scale factors, it may happen
|
||||||
|
* that the window location changes when showing a heavy weight popup window.
|
||||||
|
* E.g. when opening a dialog on the secondary screen and making combobox popup visible.
|
||||||
|
* <p>
|
||||||
|
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
|
||||||
|
*/
|
||||||
|
private static void showPopupAndFixLocation( Popup popup, Window popupWindow ) {
|
||||||
|
if( popupWindow != null ) {
|
||||||
|
// remember location of heavy weight popup window
|
||||||
|
int x = popupWindow.getX();
|
||||||
|
int y = popupWindow.getY();
|
||||||
|
|
||||||
|
popup.show();
|
||||||
|
|
||||||
|
// restore popup window location if it has changed
|
||||||
|
// (probably scaled when screens use different scale factors)
|
||||||
|
if( popupWindow.getX() != x || popupWindow.getY() != y )
|
||||||
|
popupWindow.setLocation( x, y );
|
||||||
|
} else
|
||||||
|
popup.show();
|
||||||
|
}
|
||||||
|
|
||||||
//---- class NonFlashingPopup ---------------------------------------------
|
//---- class NonFlashingPopup ---------------------------------------------
|
||||||
|
|
||||||
private static class NonFlashingPopup
|
/**
|
||||||
|
* Fixes popup background flashing effect when using dark theme on light platform theme,
|
||||||
|
* where the light popup background is shown for a fraction of a second before
|
||||||
|
* the dark popup content is shown.
|
||||||
|
* This is fixed by setting popup background to content background.
|
||||||
|
* <p>
|
||||||
|
* Defers hiding of heavy weight popup window for an event cycle,
|
||||||
|
* which allows reusing popup window to avoid flicker when "moving" popup.
|
||||||
|
*/
|
||||||
|
private class NonFlashingPopup
|
||||||
extends Popup
|
extends Popup
|
||||||
{
|
{
|
||||||
private Popup delegate;
|
private Popup delegate;
|
||||||
|
Component owner;
|
||||||
private Component contents;
|
private Component contents;
|
||||||
|
|
||||||
// heavy weight
|
// heavy weight
|
||||||
protected Window popupWindow;
|
Window popupWindow;
|
||||||
private Color oldPopupWindowBackground;
|
private Color oldPopupWindowBackground;
|
||||||
|
|
||||||
NonFlashingPopup( Popup delegate, Component contents ) {
|
private boolean disposed;
|
||||||
|
|
||||||
|
NonFlashingPopup( Popup delegate, Component owner, Component contents ) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
|
this.owner = owner;
|
||||||
this.contents = contents;
|
this.contents = contents;
|
||||||
|
|
||||||
popupWindow = SwingUtilities.windowForComponent( contents );
|
popupWindow = SwingUtilities.windowForComponent( contents );
|
||||||
@@ -485,8 +602,27 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NonFlashingPopup( NonFlashingPopup reusePopup ) {
|
||||||
|
delegate = reusePopup.delegate;
|
||||||
|
owner = reusePopup.owner;
|
||||||
|
contents = reusePopup.contents;
|
||||||
|
popupWindow = reusePopup.popupWindow;
|
||||||
|
oldPopupWindowBackground = reusePopup.oldPopupWindowBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
NonFlashingPopup cloneForReuse() {
|
||||||
|
return new NonFlashingPopup( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void show() {
|
public final void show() {
|
||||||
|
if( disposed )
|
||||||
|
return;
|
||||||
|
|
||||||
|
showImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void showImpl() {
|
||||||
if( delegate != null ) {
|
if( delegate != null ) {
|
||||||
showPopupAndFixLocation( delegate, popupWindow );
|
showPopupAndFixLocation( delegate, popupWindow );
|
||||||
|
|
||||||
@@ -510,10 +646,36 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hide() {
|
public final void hide() {
|
||||||
|
if( disposed )
|
||||||
|
return;
|
||||||
|
disposed = true;
|
||||||
|
|
||||||
|
// immediately hide non-heavy weight popups or combobox popups
|
||||||
|
if( !(popupWindow instanceof JWindow) || contents instanceof BasicComboPopup ) {
|
||||||
|
hideImpl();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// defer hiding of heavy weight popup window for an event cycle,
|
||||||
|
// which allows reusing popup window to avoid flicker when "moving" popup
|
||||||
|
((JWindow)popupWindow).getContentPane().removeAll();
|
||||||
|
stillShownHeavyWeightPopups.add( this );
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
// hide popup if it was not reused
|
||||||
|
if( stillShownHeavyWeightPopups.remove( this ) )
|
||||||
|
hideImpl();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
void hideImpl() {
|
||||||
|
if( contents instanceof JComponent )
|
||||||
|
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, null );
|
||||||
|
|
||||||
if( delegate != null ) {
|
if( delegate != null ) {
|
||||||
delegate.hide();
|
delegate.hide();
|
||||||
delegate = null;
|
delegate = null;
|
||||||
|
owner = null;
|
||||||
contents = null;
|
contents = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,6 +686,30 @@ public class FlatPopupFactory
|
|||||||
popupWindow = null;
|
popupWindow = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reset( Component contents, int ownerX, int ownerY ) {
|
||||||
|
// update popup window location
|
||||||
|
popupWindow.setLocation( ownerX, ownerY );
|
||||||
|
|
||||||
|
// replace component in content pane
|
||||||
|
Container contentPane = ((JWindow)popupWindow).getContentPane();
|
||||||
|
contentPane.removeAll();
|
||||||
|
contentPane.add( contents, BorderLayout.CENTER );
|
||||||
|
popupWindow.invalidate();
|
||||||
|
popupWindow.validate();
|
||||||
|
popupWindow.pack();
|
||||||
|
|
||||||
|
// update client property on contents
|
||||||
|
if( this.contents != contents ) {
|
||||||
|
Object old = (this.contents instanceof JComponent)
|
||||||
|
? ((JComponent)this.contents).getClientProperty( KEY_POPUP_USES_NATIVE_BORDER )
|
||||||
|
: null;
|
||||||
|
if( contents instanceof JComponent )
|
||||||
|
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, old );
|
||||||
|
|
||||||
|
this.contents = contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class DropShadowPopup ----------------------------------------------
|
//---- class DropShadowPopup ----------------------------------------------
|
||||||
@@ -531,8 +717,6 @@ public class FlatPopupFactory
|
|||||||
private class DropShadowPopup
|
private class DropShadowPopup
|
||||||
extends NonFlashingPopup
|
extends NonFlashingPopup
|
||||||
{
|
{
|
||||||
private final Component owner;
|
|
||||||
|
|
||||||
// light weight
|
// light weight
|
||||||
private JComponent lightComp;
|
private JComponent lightComp;
|
||||||
private Border oldBorder;
|
private Border oldBorder;
|
||||||
@@ -547,11 +731,11 @@ public class FlatPopupFactory
|
|||||||
// heavy weight
|
// heavy weight
|
||||||
private Popup dropShadowDelegate;
|
private Popup dropShadowDelegate;
|
||||||
private Window dropShadowWindow;
|
private Window dropShadowWindow;
|
||||||
|
private JPanel dropShadowPanel2;
|
||||||
private Color oldDropShadowWindowBackground;
|
private Color oldDropShadowWindowBackground;
|
||||||
|
|
||||||
DropShadowPopup( Popup delegate, Component owner, Component contents ) {
|
DropShadowPopup( Popup delegate, Component owner, Component contents ) {
|
||||||
super( delegate, contents );
|
super( delegate, owner, contents );
|
||||||
this.owner = owner;
|
|
||||||
|
|
||||||
Dimension size = contents.getPreferredSize();
|
Dimension size = contents.getPreferredSize();
|
||||||
if( size.width <= 0 || size.height <= 0 )
|
if( size.width <= 0 || size.height <= 0 )
|
||||||
@@ -567,24 +751,24 @@ public class FlatPopupFactory
|
|||||||
// the drop shadow and is positioned behind the popup window.
|
// the drop shadow and is positioned behind the popup window.
|
||||||
|
|
||||||
// create panel that paints the drop shadow
|
// create panel that paints the drop shadow
|
||||||
JPanel dropShadowPanel = new JPanel();
|
dropShadowPanel2 = new JPanel();
|
||||||
dropShadowPanel.setBorder( createDropShadowBorder() );
|
dropShadowPanel2.setBorder( createDropShadowBorder() );
|
||||||
dropShadowPanel.setOpaque( false );
|
dropShadowPanel2.setOpaque( false );
|
||||||
|
|
||||||
// set preferred size of drop shadow panel
|
// set preferred size of drop shadow panel
|
||||||
Dimension prefSize = popupWindow.getPreferredSize();
|
Dimension prefSize = popupWindow.getPreferredSize();
|
||||||
Insets insets = dropShadowPanel.getInsets();
|
Insets insets = dropShadowPanel2.getInsets();
|
||||||
dropShadowPanel.setPreferredSize( new Dimension(
|
dropShadowPanel2.setPreferredSize( new Dimension(
|
||||||
prefSize.width + insets.left + insets.right,
|
prefSize.width + insets.left + insets.right,
|
||||||
prefSize.height + insets.top + insets.bottom ) );
|
prefSize.height + insets.top + insets.bottom ) );
|
||||||
|
|
||||||
// create heavy weight popup for drop shadow
|
// create heavy weight popup for drop shadow
|
||||||
int x = popupWindow.getX() - insets.left;
|
int x = popupWindow.getX() - insets.left;
|
||||||
int y = popupWindow.getY() - insets.top;
|
int y = popupWindow.getY() - insets.top;
|
||||||
dropShadowDelegate = getPopupForScreenOfOwner( owner, dropShadowPanel, x, y, true );
|
dropShadowDelegate = getPopupForScreenOfOwner( owner, dropShadowPanel2, x, y, true );
|
||||||
|
|
||||||
// make drop shadow popup window translucent
|
// make drop shadow popup window translucent
|
||||||
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel );
|
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel2 );
|
||||||
if( dropShadowWindow != null ) {
|
if( dropShadowWindow != null ) {
|
||||||
oldDropShadowWindowBackground = dropShadowWindow.getBackground();
|
oldDropShadowWindowBackground = dropShadowWindow.getBackground();
|
||||||
dropShadowWindow.setBackground( new Color( 0, true ) );
|
dropShadowWindow.setBackground( new Color( 0, true ) );
|
||||||
@@ -621,6 +805,23 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DropShadowPopup( DropShadowPopup reusePopup ) {
|
||||||
|
super( reusePopup );
|
||||||
|
|
||||||
|
// not necessary to clone fields used for light/medium weight popups
|
||||||
|
|
||||||
|
// heavy weight
|
||||||
|
dropShadowDelegate = reusePopup.dropShadowDelegate;
|
||||||
|
dropShadowWindow = reusePopup.dropShadowWindow;
|
||||||
|
dropShadowPanel2 = reusePopup.dropShadowPanel2;
|
||||||
|
oldDropShadowWindowBackground = reusePopup.oldDropShadowWindowBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
NonFlashingPopup cloneForReuse() {
|
||||||
|
return new DropShadowPopup( this );
|
||||||
|
}
|
||||||
|
|
||||||
private Border createDropShadowBorder() {
|
private Border createDropShadowBorder() {
|
||||||
return new FlatDropShadowBorder(
|
return new FlatDropShadowBorder(
|
||||||
UIManager.getColor( "Popup.dropShadowColor" ),
|
UIManager.getColor( "Popup.dropShadowColor" ),
|
||||||
@@ -629,14 +830,14 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void show() {
|
void showImpl() {
|
||||||
if( dropShadowDelegate != null )
|
if( dropShadowDelegate != null )
|
||||||
showPopupAndFixLocation( dropShadowDelegate, dropShadowWindow );
|
showPopupAndFixLocation( dropShadowDelegate, dropShadowWindow );
|
||||||
|
|
||||||
if( mediumWeightPanel != null )
|
if( mediumWeightPanel != null )
|
||||||
showMediumWeightDropShadow();
|
showMediumWeightDropShadow();
|
||||||
|
|
||||||
super.show();
|
super.showImpl();
|
||||||
|
|
||||||
// fix location of light weight popup in case it has left or top drop shadow
|
// fix location of light weight popup in case it has left or top drop shadow
|
||||||
if( lightComp != null ) {
|
if( lightComp != null ) {
|
||||||
@@ -647,10 +848,11 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hide() {
|
void hideImpl() {
|
||||||
if( dropShadowDelegate != null ) {
|
if( dropShadowDelegate != null ) {
|
||||||
dropShadowDelegate.hide();
|
dropShadowDelegate.hide();
|
||||||
dropShadowDelegate = null;
|
dropShadowDelegate = null;
|
||||||
|
dropShadowPanel2 = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( mediumWeightPanel != null ) {
|
if( mediumWeightPanel != null ) {
|
||||||
@@ -659,7 +861,7 @@ public class FlatPopupFactory
|
|||||||
mediumWeightPanel = null;
|
mediumWeightPanel = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.hide();
|
super.hideImpl();
|
||||||
|
|
||||||
if( dropShadowWindow != null ) {
|
if( dropShadowWindow != null ) {
|
||||||
dropShadowWindow.setBackground( oldDropShadowWindowBackground );
|
dropShadowWindow.setBackground( oldDropShadowWindowBackground );
|
||||||
@@ -743,5 +945,25 @@ public class FlatPopupFactory
|
|||||||
if( dropShadowPanel != null && mediumWeightPanel != null )
|
if( dropShadowPanel != null && mediumWeightPanel != null )
|
||||||
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
|
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void reset( Component contents, int ownerX, int ownerY ) {
|
||||||
|
super.reset( contents, ownerX, ownerY );
|
||||||
|
|
||||||
|
if( dropShadowWindow != null ) {
|
||||||
|
// set preferred size of drop shadow panel
|
||||||
|
Dimension prefSize = popupWindow.getPreferredSize();
|
||||||
|
Insets insets = dropShadowPanel2.getInsets();
|
||||||
|
int w = prefSize.width + insets.left + insets.right;
|
||||||
|
int h = prefSize.height + insets.top + insets.bottom;
|
||||||
|
dropShadowPanel2.setPreferredSize( new Dimension( w, h ) );
|
||||||
|
|
||||||
|
// update drop shadow popup window location and size
|
||||||
|
int x = popupWindow.getX() - insets.left;
|
||||||
|
int y = popupWindow.getY() - insets.top;
|
||||||
|
dropShadowWindow.setBounds( x, y, w, h );
|
||||||
|
dropShadowWindow.pack();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.*;
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
@@ -86,6 +87,17 @@ public class FlatProgressBarUI
|
|||||||
installStyle();
|
installStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
if( !EventQueue.isDispatchThread() && progressBar.isIndeterminate() ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere(
|
||||||
|
"FlatLaf: Uninstalling indeterminate progress bar UI not on AWT thread may throw NPE in FlatProgressBarUI.paint(). Use SwingUtilities.invokeLater().",
|
||||||
|
new IllegalStateException() );
|
||||||
|
}
|
||||||
|
|
||||||
|
super.uninstallUI( c );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -110,17 +122,25 @@ public class FlatProgressBarUI
|
|||||||
|
|
||||||
propertyChangeListener = e -> {
|
propertyChangeListener = e -> {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
|
case "indeterminate":
|
||||||
|
if( !EventQueue.isDispatchThread() && !progressBar.isIndeterminate() ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere(
|
||||||
|
"FlatLaf: Using JProgressBar.setIndeterminate(false) not on AWT thread may throw NPE in FlatProgressBarUI.paint(). Use SwingUtilities.invokeLater().",
|
||||||
|
new IllegalStateException() );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case PROGRESS_BAR_LARGE_HEIGHT:
|
case PROGRESS_BAR_LARGE_HEIGHT:
|
||||||
case PROGRESS_BAR_SQUARE:
|
case PROGRESS_BAR_SQUARE:
|
||||||
progressBar.revalidate();
|
progressBar.revalidate();
|
||||||
progressBar.repaint();
|
HiDPIUtils.repaint( progressBar );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STYLE:
|
case STYLE:
|
||||||
case STYLE_CLASS:
|
case STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
progressBar.revalidate();
|
progressBar.revalidate();
|
||||||
progressBar.repaint();
|
HiDPIUtils.repaint( progressBar );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -274,6 +294,6 @@ public class FlatProgressBarUI
|
|||||||
// Only solution is to repaint whole progress bar.
|
// Only solution is to repaint whole progress bar.
|
||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( progressBar.getGraphicsConfiguration() );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( progressBar.getGraphicsConfiguration() );
|
||||||
if( (int) systemScaleFactor != systemScaleFactor )
|
if( (int) systemScaleFactor != systemScaleFactor )
|
||||||
progressBar.repaint();
|
HiDPIUtils.repaint( progressBar );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandles;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicMenuItemUI;
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
@@ -102,13 +103,23 @@ public class FlatRadioButtonMenuItemUI
|
|||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installComponents( JMenuItem menuItem ) {
|
||||||
|
super.installComponents( menuItem );
|
||||||
|
|
||||||
|
// update HTML renderer if necessary
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
|
||||||
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
return FlatHTML.createPropertyChangeListener(
|
||||||
|
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
|
||||||
|
super.createPropertyChangeListener( c ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
|
|||||||
@@ -40,12 +40,14 @@ import javax.swing.SwingUtilities;
|
|||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicButtonListener;
|
import javax.swing.plaf.basic.BasicButtonListener;
|
||||||
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.plaf.basic.BasicRadioButtonUI;
|
import javax.swing.plaf.basic.BasicRadioButtonUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
|
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -159,6 +161,10 @@ public class FlatRadioButtonUI
|
|||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
|
case BasicHTML.propertyKey:
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( b );
|
||||||
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.STYLE:
|
case FlatClientProperties.STYLE:
|
||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
||||||
@@ -168,7 +174,7 @@ public class FlatRadioButtonUI
|
|||||||
} else
|
} else
|
||||||
installStyle( b );
|
installStyle( b );
|
||||||
b.revalidate();
|
b.revalidate();
|
||||||
b.repaint();
|
HiDPIUtils.repaint( b );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +269,7 @@ public class FlatRadioButtonUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
// fill background even if not opaque if
|
// fill background even if not opaque and if:
|
||||||
// - contentAreaFilled is true and
|
// - contentAreaFilled is true and
|
||||||
// - if background color is different to default background color
|
// - if background color is different to default background color
|
||||||
// (this paints selection if using the component as cell renderer)
|
// (this paints selection if using the component as cell renderer)
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ import java.awt.Insets;
|
|||||||
import java.awt.LayoutManager;
|
import java.awt.LayoutManager;
|
||||||
import java.awt.LayoutManager2;
|
import java.awt.LayoutManager2;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.ComponentAdapter;
|
|
||||||
import java.awt.event.ComponentEvent;
|
|
||||||
import java.awt.event.ComponentListener;
|
import java.awt.event.ComponentListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
@@ -41,7 +39,6 @@ import javax.swing.JLayeredPane;
|
|||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.JRootPane;
|
import javax.swing.JRootPane;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.BorderUIResource;
|
import javax.swing.plaf.BorderUIResource;
|
||||||
@@ -88,8 +85,8 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
private Object nativeWindowBorderData;
|
private Object nativeWindowBorderData;
|
||||||
private LayoutManager oldLayout;
|
private LayoutManager oldLayout;
|
||||||
private PropertyChangeListener ancestorListener;
|
private ComponentListener macFullWindowContentListener;
|
||||||
private ComponentListener componentListener;
|
private PropertyChangeListener macWindowBackgroundListener;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatRootPaneUI();
|
return new FlatRootPaneUI();
|
||||||
@@ -107,6 +104,7 @@ public class FlatRootPaneUI
|
|||||||
installBorder();
|
installBorder();
|
||||||
|
|
||||||
installNativeWindowBorder();
|
installNativeWindowBorder();
|
||||||
|
macInstallFullWindowContentSupport();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void installBorder() {
|
protected void installBorder() {
|
||||||
@@ -123,6 +121,7 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
uninstallNativeWindowBorder();
|
uninstallNativeWindowBorder();
|
||||||
uninstallClientDecorations();
|
uninstallClientDecorations();
|
||||||
|
macUninstallFullWindowContentSupport();
|
||||||
rootPane = null;
|
rootPane = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,9 +156,7 @@ public class FlatRootPaneUI
|
|||||||
parent.setBackground( UIManager.getColor( "control" ) );
|
parent.setBackground( UIManager.getColor( "control" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable dark window appearance on macOS when running in JetBrains Runtime
|
macClearBackgroundForTranslucentWindow( c );
|
||||||
if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater )
|
|
||||||
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -179,55 +176,20 @@ public class FlatRootPaneUI
|
|||||||
protected void installListeners( JRootPane root ) {
|
protected void installListeners( JRootPane root ) {
|
||||||
super.installListeners( root );
|
super.installListeners( root );
|
||||||
|
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
if( SystemInfo.isMacFullWindowContentSupported )
|
||||||
// On HiDPI screens, where scaling is used, there may be white lines on the
|
macFullWindowContentListener = FullWindowContentSupport.macInstallListeners( root );
|
||||||
// bottom and on the right side of the window when it is initially shown.
|
macInstallWindowBackgroundListener( root );
|
||||||
// This is very disturbing in dark themes, but hard to notice in light themes.
|
|
||||||
// Seems to be a rounding issue when Swing adds dirty region of window
|
|
||||||
// using RepaintManager.nativeAddDirtyRegion().
|
|
||||||
//
|
|
||||||
// Note: Not using a HierarchyListener here, which would be much easier,
|
|
||||||
// because this causes problems with mouse clicks in heavy-weight popups.
|
|
||||||
// Instead, add a listener to the root pane that waits until it is added
|
|
||||||
// to a window, then add a component listener to the window.
|
|
||||||
// See: https://github.com/JFormDesigner/FlatLaf/issues/371
|
|
||||||
ancestorListener = e -> {
|
|
||||||
Object oldValue = e.getOldValue();
|
|
||||||
Object newValue = e.getNewValue();
|
|
||||||
if( newValue instanceof Window ) {
|
|
||||||
if( componentListener == null ) {
|
|
||||||
componentListener = new ComponentAdapter() {
|
|
||||||
@Override
|
|
||||||
public void componentShown( ComponentEvent e ) {
|
|
||||||
// add whole root pane to dirty regions when window is initially shown
|
|
||||||
root.getParent().repaint( root.getX(), root.getY(), root.getWidth(), root.getHeight() );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
((Window)newValue).addComponentListener( componentListener );
|
|
||||||
} else if( newValue == null && oldValue instanceof Window ) {
|
|
||||||
if( componentListener != null )
|
|
||||||
((Window)oldValue).removeComponentListener( componentListener );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
root.addPropertyChangeListener( "ancestor", ancestorListener );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void uninstallListeners( JRootPane root ) {
|
protected void uninstallListeners( JRootPane root ) {
|
||||||
super.uninstallListeners( root );
|
super.uninstallListeners( root );
|
||||||
|
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
if( SystemInfo.isMacFullWindowContentSupported ) {
|
||||||
if( componentListener != null ) {
|
FullWindowContentSupport.macUninstallListeners( root, macFullWindowContentListener );
|
||||||
Window window = SwingUtilities.windowForComponent( root );
|
macFullWindowContentListener = null;
|
||||||
if( window != null )
|
|
||||||
window.removeComponentListener( componentListener );
|
|
||||||
componentListener = null;
|
|
||||||
}
|
|
||||||
root.removePropertyChangeListener( "ancestor", ancestorListener );
|
|
||||||
ancestorListener = null;
|
|
||||||
}
|
}
|
||||||
|
macUninstallWindowBackgroundListener( root );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 1.1.2 */
|
/** @since 1.1.2 */
|
||||||
@@ -307,19 +269,141 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
// layer title pane under frame content layer to allow placing menu bar over title pane
|
// layer title pane under frame content layer to allow placing menu bar over title pane
|
||||||
protected final static Integer TITLE_PANE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 1;
|
protected final static Integer TITLE_PANE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 1;
|
||||||
|
private final static Integer TITLE_PANE_MOUSE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 2;
|
||||||
|
private final static Integer WINDOW_TOP_BORDER_LAYER = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
// for fullWindowContent mode, layer title pane over frame content layer to allow placing title bar buttons over content
|
||||||
|
/** @since 3.4 */
|
||||||
|
protected final static Integer TITLE_PANE_FULL_WINDOW_CONTENT_LAYER = JLayeredPane.FRAME_CONTENT_LAYER + 1;
|
||||||
|
|
||||||
|
private Integer getLayerForTitlePane() {
|
||||||
|
return isFullWindowContent( rootPane ) ? TITLE_PANE_FULL_WINDOW_CONTENT_LAYER : TITLE_PANE_LAYER;
|
||||||
|
}
|
||||||
|
|
||||||
protected void setTitlePane( FlatTitlePane newTitlePane ) {
|
protected void setTitlePane( FlatTitlePane newTitlePane ) {
|
||||||
JLayeredPane layeredPane = rootPane.getLayeredPane();
|
JLayeredPane layeredPane = rootPane.getLayeredPane();
|
||||||
|
|
||||||
if( titlePane != null )
|
if( titlePane != null ) {
|
||||||
layeredPane.remove( titlePane );
|
layeredPane.remove( titlePane );
|
||||||
|
layeredPane.remove( titlePane.mouseLayer );
|
||||||
|
if( titlePane.windowTopBorderLayer != null )
|
||||||
|
layeredPane.remove( titlePane.windowTopBorderLayer );
|
||||||
|
}
|
||||||
|
|
||||||
if( newTitlePane != null )
|
if( newTitlePane != null ) {
|
||||||
layeredPane.add( newTitlePane, TITLE_PANE_LAYER );
|
layeredPane.add( newTitlePane, getLayerForTitlePane() );
|
||||||
|
layeredPane.add( newTitlePane.mouseLayer, TITLE_PANE_MOUSE_LAYER );
|
||||||
|
if( newTitlePane.windowTopBorderLayer != null && newTitlePane.isWindowTopBorderNeeded() && isFullWindowContent( rootPane ) )
|
||||||
|
layeredPane.add( newTitlePane.windowTopBorderLayer, WINDOW_TOP_BORDER_LAYER );
|
||||||
|
}
|
||||||
|
|
||||||
titlePane = newTitlePane;
|
titlePane = newTitlePane;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void macInstallFullWindowContentSupport() {
|
||||||
|
if( !SystemInfo.isMacOS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// set window buttons spacing
|
||||||
|
if( isMacButtonsSpacingSupported() && rootPane.isDisplayable() ) {
|
||||||
|
int buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT;
|
||||||
|
String value = (String) rootPane.getClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING );
|
||||||
|
if( value != null ) {
|
||||||
|
switch( value ) {
|
||||||
|
case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_MEDIUM:
|
||||||
|
buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_MEDIUM;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_LARGE:
|
||||||
|
buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_LARGE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow( rootPane ), buttonsSpacing );
|
||||||
|
}
|
||||||
|
|
||||||
|
// update buttons bounds client property
|
||||||
|
FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty( rootPane );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void macUninstallFullWindowContentSupport() {
|
||||||
|
if( !SystemInfo.isMacOS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// do not uninstall when switching to another FlatLaf theme
|
||||||
|
if( UIManager.getLookAndFeel() instanceof FlatLaf )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// reset window buttons spacing
|
||||||
|
if( isMacButtonsSpacingSupported() )
|
||||||
|
FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow( rootPane ), FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT );
|
||||||
|
|
||||||
|
// remove buttons bounds client property
|
||||||
|
FullWindowContentSupport.macUninstallFullWindowContentButtonsBoundsProperty( rootPane );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMacButtonsSpacingSupported() {
|
||||||
|
return SystemInfo.isMacOS && SystemInfo.isJava_17_orLater && FlatNativeMacLibrary.isLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void macInstallWindowBackgroundListener( JRootPane c ) {
|
||||||
|
if( !SystemInfo.isMacOS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Window window = getParentWindow( c );
|
||||||
|
if( window != null && macWindowBackgroundListener == null ) {
|
||||||
|
macWindowBackgroundListener = e -> macClearBackgroundForTranslucentWindow( c );
|
||||||
|
window.addPropertyChangeListener( "background", macWindowBackgroundListener );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void macUninstallWindowBackgroundListener( JRootPane c ) {
|
||||||
|
if( !SystemInfo.isMacOS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Window window = getParentWindow( c );
|
||||||
|
if( window != null && macWindowBackgroundListener != null ) {
|
||||||
|
window.removePropertyChangeListener( "background", macWindowBackgroundListener );
|
||||||
|
macWindowBackgroundListener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When setting window background to translucent color (alpha < 255),
|
||||||
|
* Swing paints that window translucent on Windows and Linux, but not on macOS.
|
||||||
|
* The reason for this is that FlatLaf sets the background color of the root pane,
|
||||||
|
* and Swing behaves a bit differently on macOS than on other platforms in that case.
|
||||||
|
* Other L&Fs do not set root pane background, which is {@code null} by default.
|
||||||
|
* <p>
|
||||||
|
* To fix this problem, set the root pane background to {@code null}
|
||||||
|
* if windows uses a translucent background.
|
||||||
|
*/
|
||||||
|
private void macClearBackgroundForTranslucentWindow( JRootPane c ) {
|
||||||
|
if( !SystemInfo.isMacOS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Window window = getParentWindow( c );
|
||||||
|
if( window != null ) {
|
||||||
|
Color windowBackground = window.getBackground();
|
||||||
|
if( windowBackground != null &&
|
||||||
|
windowBackground.getAlpha() < 255 &&
|
||||||
|
c.getBackground() instanceof UIResource )
|
||||||
|
{
|
||||||
|
// clear root pane background
|
||||||
|
c.setBackground( null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Window getParentWindow( JRootPane c ) {
|
||||||
|
// not using SwingUtilities.windowForComponent() or SwingUtilities.getWindowAncestor()
|
||||||
|
// here because root panes may be nested and used anywhere (e.g. in JInternalFrame)
|
||||||
|
// but we're only interested in the "root" root pane, which is a direct child of the window
|
||||||
|
Container parent = c.getParent();
|
||||||
|
return (parent instanceof Window) ? (Window) parent : null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
@@ -364,6 +448,33 @@ public class FlatRootPaneUI
|
|||||||
titlePane.titleBarColorsChanged();
|
titlePane.titleBarColorsChanged();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.TITLE_BAR_HEIGHT:
|
||||||
|
if( titlePane != null )
|
||||||
|
titlePane.revalidate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.FULL_WINDOW_CONTENT:
|
||||||
|
if( titlePane != null ) {
|
||||||
|
rootPane.getLayeredPane().setLayer( titlePane, getLayerForTitlePane() );
|
||||||
|
if( titlePane.windowTopBorderLayer != null ) {
|
||||||
|
JLayeredPane layeredPane = rootPane.getLayeredPane();
|
||||||
|
if( titlePane.isWindowTopBorderNeeded() && isFullWindowContent( rootPane ) )
|
||||||
|
layeredPane.add( titlePane.windowTopBorderLayer, WINDOW_TOP_BORDER_LAYER );
|
||||||
|
else
|
||||||
|
layeredPane.remove( titlePane.windowTopBorderLayer );
|
||||||
|
}
|
||||||
|
titlePane.updateIcon();
|
||||||
|
titlePane.updateVisibility();
|
||||||
|
titlePane.updateFullWindowContentButtonsBoundsProperty();
|
||||||
|
}
|
||||||
|
FullWindowContentSupport.revalidatePlaceholders( rootPane );
|
||||||
|
rootPane.revalidate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS:
|
||||||
|
FullWindowContentSupport.revalidatePlaceholders( rootPane );
|
||||||
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.GLASS_PANE_FULL_HEIGHT:
|
case FlatClientProperties.GLASS_PANE_FULL_HEIGHT:
|
||||||
rootPane.revalidate();
|
rootPane.revalidate();
|
||||||
break;
|
break;
|
||||||
@@ -372,14 +483,43 @@ public class FlatRootPaneUI
|
|||||||
if( rootPane.isDisplayable() )
|
if( rootPane.isDisplayable() )
|
||||||
throw new IllegalComponentStateException( "The client property 'Window.style' must be set before the window becomes displayable." );
|
throw new IllegalComponentStateException( "The client property 'Window.style' must be set before the window becomes displayable." );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "ancestor":
|
||||||
|
if( e.getNewValue() instanceof Window )
|
||||||
|
macClearBackgroundForTranslucentWindow( rootPane );
|
||||||
|
|
||||||
|
macUninstallWindowBackgroundListener( rootPane );
|
||||||
|
macInstallWindowBackgroundListener( rootPane );
|
||||||
|
|
||||||
|
// FlatNativeMacLibrary.setWindowButtonsSpacing() and
|
||||||
|
// FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty()
|
||||||
|
// require a native window, but setting the client properties
|
||||||
|
// "apple.awt.fullWindowContent" or FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING
|
||||||
|
// is usually done before the native window is created
|
||||||
|
// --> try again when native window is created
|
||||||
|
if( e.getNewValue() instanceof Window )
|
||||||
|
macInstallFullWindowContentSupport();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING:
|
||||||
|
macInstallFullWindowContentSupport();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "apple.awt.fullWindowContent":
|
||||||
|
if( SystemInfo.isMacFullWindowContentSupported )
|
||||||
|
FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty( rootPane );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.4 */
|
||||||
|
protected static boolean isFullWindowContent( JRootPane rootPane ) {
|
||||||
|
return FlatClientProperties.clientPropertyBoolean( rootPane, FlatClientProperties.FULL_WINDOW_CONTENT, false );
|
||||||
|
}
|
||||||
|
|
||||||
protected static boolean isMenuBarEmbedded( JRootPane rootPane ) {
|
protected static boolean isMenuBarEmbedded( JRootPane rootPane ) {
|
||||||
RootPaneUI ui = rootPane.getUI();
|
FlatTitlePane titlePane = getTitlePane( rootPane );
|
||||||
return ui instanceof FlatRootPaneUI &&
|
return titlePane != null && titlePane.isMenuBarEmbedded();
|
||||||
((FlatRootPaneUI)ui).titlePane != null &&
|
|
||||||
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2.4 */
|
/** @since 2.4 */
|
||||||
@@ -415,24 +555,22 @@ public class FlatRootPaneUI
|
|||||||
private Dimension computeLayoutSize( Container parent, Function<Component, Dimension> getSizeFunc ) {
|
private Dimension computeLayoutSize( Container parent, Function<Component, Dimension> getSizeFunc ) {
|
||||||
JRootPane rootPane = (JRootPane) parent;
|
JRootPane rootPane = (JRootPane) parent;
|
||||||
|
|
||||||
Dimension titlePaneSize = (titlePane != null)
|
|
||||||
? getSizeFunc.apply( titlePane )
|
|
||||||
: new Dimension();
|
|
||||||
Dimension contentSize = (rootPane.getContentPane() != null)
|
Dimension contentSize = (rootPane.getContentPane() != null)
|
||||||
? getSizeFunc.apply( rootPane.getContentPane() )
|
? getSizeFunc.apply( rootPane.getContentPane() )
|
||||||
: rootPane.getSize();
|
: rootPane.getSize(); // same as in JRootPane.RootLayout.preferredLayoutSize()
|
||||||
|
|
||||||
int width = contentSize.width; // title pane width is not considered here
|
int width = contentSize.width; // title pane width is not considered here
|
||||||
int height = titlePaneSize.height + contentSize.height;
|
int height = contentSize.height;
|
||||||
|
if( titlePane != null && !isFullWindowContent( rootPane ) )
|
||||||
|
height += getSizeFunc.apply( titlePane ).height;
|
||||||
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
||||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
Dimension menuBarSize = (menuBar != null && menuBar.isVisible())
|
if( menuBar != null && menuBar.isVisible() ) {
|
||||||
? getSizeFunc.apply( menuBar )
|
Dimension menuBarSize = getSizeFunc.apply( menuBar );
|
||||||
: new Dimension();
|
|
||||||
|
|
||||||
width = Math.max( width, menuBarSize.width );
|
width = Math.max( width, menuBarSize.width );
|
||||||
height += menuBarSize.height;
|
height += menuBarSize.height;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Insets insets = rootPane.getInsets();
|
Insets insets = rootPane.getInsets();
|
||||||
|
|
||||||
@@ -456,11 +594,28 @@ public class FlatRootPaneUI
|
|||||||
if( rootPane.getLayeredPane() != null )
|
if( rootPane.getLayeredPane() != null )
|
||||||
rootPane.getLayeredPane().setBounds( x, y, width, height );
|
rootPane.getLayeredPane().setBounds( x, y, width, height );
|
||||||
|
|
||||||
// title pane
|
// title pane (is a child of layered pane)
|
||||||
int nextY = 0;
|
int nextY = 0;
|
||||||
if( titlePane != null ) {
|
if( titlePane != null ) {
|
||||||
int prefHeight = !isFullScreen ? titlePane.getPreferredSize().height : 0;
|
int prefHeight = !isFullScreen ? titlePane.getPreferredSize().height : 0;
|
||||||
|
boolean isFullWindowContent = isFullWindowContent( rootPane );
|
||||||
|
if( isFullWindowContent && !UIManager.getBoolean( FlatTitlePane.KEY_DEBUG_SHOW_RECTANGLES ) ) {
|
||||||
|
// place title bar into top-right corner
|
||||||
|
int tw = Math.min( titlePane.getPreferredSize().width, width );
|
||||||
|
int tx = titlePane.getComponentOrientation().isLeftToRight() ? width - tw : 0;
|
||||||
|
titlePane.setBounds( tx, 0, tw, prefHeight );
|
||||||
|
} else
|
||||||
titlePane.setBounds( 0, 0, width, prefHeight );
|
titlePane.setBounds( 0, 0, width, prefHeight );
|
||||||
|
|
||||||
|
titlePane.mouseLayer.setBounds( 0, 0, width, prefHeight );
|
||||||
|
if( titlePane.windowTopBorderLayer != null ) {
|
||||||
|
boolean show = isFullWindowContent && !titlePane.isWindowMaximized() && !isFullScreen;
|
||||||
|
if( show )
|
||||||
|
titlePane.windowTopBorderLayer.setBounds( 0, 0, width, 1 );
|
||||||
|
titlePane.windowTopBorderLayer.setVisible( show );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !isFullWindowContent )
|
||||||
nextY += prefHeight;
|
nextY += prefHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,7 +627,7 @@ public class FlatRootPaneUI
|
|||||||
rootPane.getGlassPane().setBounds( x, y + offset, width, height - offset );
|
rootPane.getGlassPane().setBounds( x, y + offset, width, height - offset );
|
||||||
}
|
}
|
||||||
|
|
||||||
// menu bar
|
// menu bar (is a child of layered pane)
|
||||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
if( menuBar != null && menuBar.isVisible() ) {
|
if( menuBar != null && menuBar.isVisible() ) {
|
||||||
boolean embedded = !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded();
|
boolean embedded = !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded();
|
||||||
@@ -480,13 +635,23 @@ public class FlatRootPaneUI
|
|||||||
titlePane.validate();
|
titlePane.validate();
|
||||||
menuBar.setBounds( titlePane.getMenuBarBounds() );
|
menuBar.setBounds( titlePane.getMenuBarBounds() );
|
||||||
} else {
|
} else {
|
||||||
|
int mx = 0;
|
||||||
|
int mw = width;
|
||||||
|
if( titlePane != null && isFullWindowContent( rootPane ) ) {
|
||||||
|
// make menu bar width smaller to avoid that it overlaps title bar buttons
|
||||||
|
int tw = Math.min( titlePane.getPreferredSize().width, width );
|
||||||
|
mw -= tw;
|
||||||
|
if( !titlePane.getComponentOrientation().isLeftToRight() )
|
||||||
|
mx = tw;
|
||||||
|
}
|
||||||
|
|
||||||
Dimension prefSize = menuBar.getPreferredSize();
|
Dimension prefSize = menuBar.getPreferredSize();
|
||||||
menuBar.setBounds( 0, nextY, width, prefSize.height );
|
menuBar.setBounds( mx, nextY, mw, prefSize.height );
|
||||||
nextY += prefSize.height;
|
nextY += prefSize.height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// content pane
|
// content pane (is a child of layered pane)
|
||||||
Container contentPane = rootPane.getContentPane();
|
Container contentPane = rootPane.getContentPane();
|
||||||
if( contentPane != null )
|
if( contentPane != null )
|
||||||
contentPane.setBounds( 0, nextY, width, Math.max( height - nextY, 0 ) );
|
contentPane.setBounds( 0, nextY, width, Math.max( height - nextY, 0 ) );
|
||||||
@@ -499,7 +664,7 @@ public class FlatRootPaneUI
|
|||||||
@Override
|
@Override
|
||||||
public void invalidateLayout( Container parent ) {
|
public void invalidateLayout( Container parent ) {
|
||||||
if( titlePane != null )
|
if( titlePane != null )
|
||||||
titlePane.menuBarChanged();
|
titlePane.menuBarInvalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
|||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -212,14 +213,14 @@ public class FlatScrollBarUI
|
|||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
||||||
scrollbar.revalidate();
|
scrollbar.revalidate();
|
||||||
scrollbar.repaint();
|
HiDPIUtils.repaint( scrollbar );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.STYLE:
|
case FlatClientProperties.STYLE:
|
||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
scrollbar.revalidate();
|
scrollbar.revalidate();
|
||||||
scrollbar.repaint();
|
HiDPIUtils.repaint( scrollbar );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "componentOrientation":
|
case "componentOrientation":
|
||||||
@@ -245,7 +246,7 @@ public class FlatScrollBarUI
|
|||||||
// because scroll bars do not receive mouse exited event.
|
// because scroll bars do not receive mouse exited event.
|
||||||
// The scroll pane, including its scroll bars, is not part
|
// The scroll pane, including its scroll bars, is not part
|
||||||
// of the component hierarchy and does not receive mouse events
|
// of the component hierarchy and does not receive mouse events
|
||||||
// directly. Instead LWComponentPeer receives mouse events
|
// directly. Instead, LWComponentPeer receives mouse events
|
||||||
// and delegates them to peers, but entered/exited events
|
// and delegates them to peers, but entered/exited events
|
||||||
// are sent only for the whole scroll pane.
|
// are sent only for the whole scroll pane.
|
||||||
// Exited event is only sent when mouse leaves scroll pane.
|
// Exited event is only sent when mouse leaves scroll pane.
|
||||||
@@ -492,7 +493,7 @@ public class FlatScrollBarUI
|
|||||||
|
|
||||||
private void repaint() {
|
private void repaint() {
|
||||||
if( scrollbar.isEnabled() )
|
if( scrollbar.isEnabled() )
|
||||||
scrollbar.repaint();
|
HiDPIUtils.repaint( scrollbar );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.JTree;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.text.JTextComponent;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Border for {@link javax.swing.JScrollPane}.
|
||||||
|
*
|
||||||
|
* @uiDefault ScrollPane.arc int
|
||||||
|
* @uiDefault ScrollPane.List.arc int
|
||||||
|
* @uiDefault ScrollPane.Table.arc int
|
||||||
|
* @uiDefault ScrollPane.TextComponent.arc int
|
||||||
|
* @uiDefault ScrollPane.Tree.arc int
|
||||||
|
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public class FlatScrollPaneBorder
|
||||||
|
extends FlatBorder
|
||||||
|
{
|
||||||
|
@Styleable protected int arc = UIManager.getInt( "ScrollPane.arc" );
|
||||||
|
|
||||||
|
private boolean isArcStyled;
|
||||||
|
private final int listArc = FlatUIUtils.getUIInt( "ScrollPane.List.arc", -1 );
|
||||||
|
private final int tableArc = FlatUIUtils.getUIInt( "ScrollPane.Table.arc", -1 );
|
||||||
|
private final int textComponentArc = FlatUIUtils.getUIInt( "ScrollPane.TextComponent.arc", -1 );
|
||||||
|
private final int treeArc = FlatUIUtils.getUIInt( "ScrollPane.Tree.arc", -1 );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
Object oldValue = super.applyStyleProperty( key, value );
|
||||||
|
|
||||||
|
if( "arc".equals( key ) )
|
||||||
|
isArcStyled = true;
|
||||||
|
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
|
insets = super.getBorderInsets( c, insets );
|
||||||
|
|
||||||
|
// if view is rounded, increase left and right insets to avoid that the viewport
|
||||||
|
// is painted over the rounded border on the corners
|
||||||
|
int padding = getLeftRightPadding( c );
|
||||||
|
if( padding > 0 ) {
|
||||||
|
insets.left += padding;
|
||||||
|
insets.right += padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
return insets;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getArc( Component c ) {
|
||||||
|
if( isCellEditor( c ) )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if( isArcStyled )
|
||||||
|
return arc;
|
||||||
|
|
||||||
|
if( c instanceof JScrollPane ) {
|
||||||
|
Component view = FlatScrollPaneUI.getView( (JScrollPane) c );
|
||||||
|
if( listArc >= 0 && view instanceof JList )
|
||||||
|
return listArc;
|
||||||
|
if( tableArc >= 0 && view instanceof JTable )
|
||||||
|
return tableArc;
|
||||||
|
if( textComponentArc >= 0&& view instanceof JTextComponent )
|
||||||
|
return textComponentArc;
|
||||||
|
if( treeArc >= 0 && view instanceof JTree )
|
||||||
|
return treeArc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scaled left/right padding used when arc is larger than zero.
|
||||||
|
* <p>
|
||||||
|
* This is the distance from the inside of the left border to the left side of the view component.
|
||||||
|
* On the right side, this is the distance between the right side of the view component and
|
||||||
|
* the vertical scrollbar. Or the inside of the right border if the scrollbar is hidden.
|
||||||
|
*/
|
||||||
|
public int getLeftRightPadding( Component c ) {
|
||||||
|
// Subtract lineWidth from radius because radius is given for the outside
|
||||||
|
// of the painted line, but insets from super already include lineWidth.
|
||||||
|
// Reduce padding by 10% to make padding slightly smaller because it is not recognizable
|
||||||
|
// when the view is minimally painted over the beginning of the border curve.
|
||||||
|
int arc = getArc( c );
|
||||||
|
return (arc > 0)
|
||||||
|
? Math.max( Math.round( UIScale.scale( ((arc / 2f) - getLineWidth( c )) * 0.9f ) ), 0 )
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,9 +17,12 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.KeyboardFocusManager;
|
import java.awt.KeyboardFocusManager;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.ContainerEvent;
|
import java.awt.event.ContainerEvent;
|
||||||
import java.awt.event.ContainerListener;
|
import java.awt.event.ContainerListener;
|
||||||
@@ -41,16 +44,20 @@ import javax.swing.JTree;
|
|||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.ScrollPaneConstants;
|
import javax.swing.ScrollPaneConstants;
|
||||||
|
import javax.swing.ScrollPaneLayout;
|
||||||
import javax.swing.Scrollable;
|
import javax.swing.Scrollable;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicScrollPaneUI;
|
import javax.swing.plaf.basic.BasicScrollPaneUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
|
||||||
@@ -97,7 +104,13 @@ public class FlatScrollPaneUI
|
|||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
|
int arc = UIManager.getInt( "ScrollPane.arc" );
|
||||||
|
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 && arc == 0 );
|
||||||
|
|
||||||
|
// install layout manager
|
||||||
|
LayoutManager layout = c.getLayout();
|
||||||
|
if( layout != null && layout.getClass() == ScrollPaneLayout.UIResource.class )
|
||||||
|
c.setLayout( createScrollPaneLayout() );
|
||||||
|
|
||||||
installStyle();
|
installStyle();
|
||||||
|
|
||||||
@@ -108,6 +121,10 @@ public class FlatScrollPaneUI
|
|||||||
public void uninstallUI( JComponent c ) {
|
public void uninstallUI( JComponent c ) {
|
||||||
MigLayoutVisualPadding.uninstall( scrollpane );
|
MigLayoutVisualPadding.uninstall( scrollpane );
|
||||||
|
|
||||||
|
// uninstall layout manager
|
||||||
|
if( c.getLayout() instanceof FlatScrollPaneLayout )
|
||||||
|
c.setLayout( new ScrollPaneLayout.UIResource() );
|
||||||
|
|
||||||
super.uninstallUI( c );
|
super.uninstallUI( c );
|
||||||
|
|
||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
@@ -130,6 +147,13 @@ public class FlatScrollPaneUI
|
|||||||
handler = null;
|
handler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
protected FlatScrollPaneLayout createScrollPaneLayout() {
|
||||||
|
return new FlatScrollPaneLayout();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected MouseWheelListener createMouseWheelListener() {
|
protected MouseWheelListener createMouseWheelListener() {
|
||||||
MouseWheelListener superListener = super.createMouseWheelListener();
|
MouseWheelListener superListener = super.createMouseWheelListener();
|
||||||
@@ -186,7 +210,7 @@ public class FlatScrollPaneUI
|
|||||||
|
|
||||||
// Use (0, 0) view position to obtain a constant unit increment of first item.
|
// Use (0, 0) view position to obtain a constant unit increment of first item.
|
||||||
// Unit increment may be different for each item.
|
// Unit increment may be different for each item.
|
||||||
Rectangle visibleRect = new Rectangle( viewport.getViewSize() );
|
Rectangle visibleRect = new Rectangle( viewport.getExtentSize() );
|
||||||
unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
|
unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
|
||||||
|
|
||||||
if( unitIncrement > 0 ) {
|
if( unitIncrement > 0 ) {
|
||||||
@@ -274,11 +298,11 @@ public class FlatScrollPaneUI
|
|||||||
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
|
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
|
||||||
if( vsb != null ) {
|
if( vsb != null ) {
|
||||||
vsb.revalidate();
|
vsb.revalidate();
|
||||||
vsb.repaint();
|
HiDPIUtils.repaint( vsb );
|
||||||
}
|
}
|
||||||
if( hsb != null ) {
|
if( hsb != null ) {
|
||||||
hsb.revalidate();
|
hsb.revalidate();
|
||||||
hsb.repaint();
|
HiDPIUtils.repaint( hsb );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -290,8 +314,7 @@ public class FlatScrollPaneUI
|
|||||||
Object corner = e.getNewValue();
|
Object corner = e.getNewValue();
|
||||||
if( corner instanceof JButton &&
|
if( corner instanceof JButton &&
|
||||||
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
|
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
|
||||||
scrollpane.getViewport() != null &&
|
getView( scrollpane ) instanceof JTable )
|
||||||
scrollpane.getViewport().getView() instanceof JTable )
|
|
||||||
{
|
{
|
||||||
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
|
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
|
||||||
((JButton)corner).setFocusable( false );
|
((JButton)corner).setFocusable( false );
|
||||||
@@ -299,14 +322,26 @@ public class FlatScrollPaneUI
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.OUTLINE:
|
case FlatClientProperties.OUTLINE:
|
||||||
scrollpane.repaint();
|
HiDPIUtils.repaint( scrollpane );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.STYLE:
|
case FlatClientProperties.STYLE:
|
||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
scrollpane.revalidate();
|
scrollpane.revalidate();
|
||||||
scrollpane.repaint();
|
HiDPIUtils.repaint( scrollpane );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "border":
|
||||||
|
Object newBorder = e.getNewValue();
|
||||||
|
if( newBorder != null && newBorder == UIManager.getBorder( "Table.scrollPaneBorder" ) ) {
|
||||||
|
// JTable.configureEnclosingScrollPaneUI() replaces the scrollpane border
|
||||||
|
// with another one --> re-apply style on new border
|
||||||
|
borderShared = null;
|
||||||
|
installStyle();
|
||||||
|
scrollpane.revalidate();
|
||||||
|
HiDPIUtils.repaint( scrollpane );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -334,9 +369,10 @@ public class FlatScrollPaneUI
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
if( key.equals( "focusWidth" ) ) {
|
if( key.equals( "focusWidth" ) || key.equals( "arc" ) ) {
|
||||||
int focusWidth = (value instanceof Integer) ? (int) value : UIManager.getInt( "Component.focusWidth" );
|
int focusWidth = (value instanceof Integer) ? (int) value : UIManager.getInt( "Component.focusWidth" );
|
||||||
LookAndFeel.installProperty( scrollpane, "opaque", focusWidth == 0 );
|
int arc = (value instanceof Integer) ? (int) value : UIManager.getInt( "ScrollPane.arc" );
|
||||||
|
LookAndFeel.installProperty( scrollpane, "opaque", focusWidth == 0 && arc == 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( borderShared == null )
|
if( borderShared == null )
|
||||||
@@ -402,13 +438,46 @@ public class FlatScrollPaneUI
|
|||||||
c.getHeight() - insets.top - insets.bottom );
|
c.getHeight() - insets.top - insets.bottom );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if view is rounded, paint rounded background with view background color
|
||||||
|
// to ensure that free areas at left and right have same color as view
|
||||||
|
Component view;
|
||||||
|
float arc = getBorderArc( scrollpane );
|
||||||
|
if( arc > 0 && (view = getView( scrollpane )) != null ) {
|
||||||
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
|
|
||||||
|
g.setColor( view.getBackground() );
|
||||||
|
|
||||||
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
}
|
||||||
|
|
||||||
paint( g, c );
|
paint( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
Border viewportBorder = scrollpane.getViewportBorder();
|
||||||
|
if( viewportBorder != null ) {
|
||||||
|
Rectangle r = scrollpane.getViewportBorderBounds();
|
||||||
|
int padding = getBorderLeftRightPadding( scrollpane );
|
||||||
|
JScrollBar vsb = scrollpane.getVerticalScrollBar();
|
||||||
|
if( padding > 0 &&
|
||||||
|
vsb != null && vsb.isVisible() &&
|
||||||
|
scrollpane.getLayout() instanceof FlatScrollPaneLayout &&
|
||||||
|
((FlatScrollPaneLayout)scrollpane.getLayout()).canIncreaseViewportWidth( scrollpane ) )
|
||||||
|
{
|
||||||
|
boolean ltr = scrollpane.getComponentOrientation().isLeftToRight();
|
||||||
|
int extraWidth = Math.min( padding, vsb.getWidth() );
|
||||||
|
viewportBorder.paintBorder( scrollpane, g, r.x - (ltr ? 0 : extraWidth), r.y, r.width + extraWidth, r.height );
|
||||||
|
} else
|
||||||
|
viewportBorder.paintBorder( scrollpane, g, r.x, r.y, r.width, r.height );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 1.3 */
|
/** @since 1.3 */
|
||||||
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
|
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
|
||||||
JViewport viewport = scrollPane.getViewport();
|
Component view = getView( scrollPane );
|
||||||
Component view = (viewport != null) ? viewport.getView() : null;
|
|
||||||
if( view == null )
|
if( view == null )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -428,6 +497,25 @@ public class FlatScrollPaneUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Component getView( JScrollPane scrollPane ) {
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
return (viewport != null) ? viewport.getView() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getBorderArc( JScrollPane scrollPane ) {
|
||||||
|
Border border = scrollPane.getBorder();
|
||||||
|
return (border instanceof FlatScrollPaneBorder)
|
||||||
|
? UIScale.scale( (float) ((FlatScrollPaneBorder)border).getArc( scrollPane ) )
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getBorderLeftRightPadding( JScrollPane scrollPane ) {
|
||||||
|
Border border = scrollPane.getBorder();
|
||||||
|
return (border instanceof FlatScrollPaneBorder)
|
||||||
|
? ((FlatScrollPaneBorder)border).getLeftRightPadding( scrollPane )
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class Handler ------------------------------------------------------
|
//---- class Handler ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -450,13 +538,71 @@ public class FlatScrollPaneUI
|
|||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
// necessary to update focus border
|
// necessary to update focus border
|
||||||
scrollpane.repaint();
|
if( scrollpane.getBorder() instanceof FlatBorder )
|
||||||
|
HiDPIUtils.repaint( scrollpane );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
// necessary to update focus border
|
// necessary to update focus border
|
||||||
scrollpane.repaint();
|
if( scrollpane.getBorder() instanceof FlatBorder )
|
||||||
|
HiDPIUtils.repaint( scrollpane );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatScrollPaneLayout -----------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
protected static class FlatScrollPaneLayout
|
||||||
|
extends ScrollPaneLayout.UIResource
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void layoutContainer( Container parent ) {
|
||||||
|
super.layoutContainer( parent );
|
||||||
|
|
||||||
|
JScrollPane scrollPane = (JScrollPane) parent;
|
||||||
|
int padding = getBorderLeftRightPadding( scrollPane );
|
||||||
|
if( padding > 0 && vsb != null && vsb.isVisible() ) {
|
||||||
|
// move vertical scrollbar to trailing edge
|
||||||
|
Insets insets = scrollPane.getInsets();
|
||||||
|
Rectangle r = vsb.getBounds();
|
||||||
|
int y = Math.max( r.y, insets.top + padding );
|
||||||
|
int y2 = Math.min( r.y + r.height, scrollPane.getHeight() - insets.bottom - padding );
|
||||||
|
boolean ltr = scrollPane.getComponentOrientation().isLeftToRight();
|
||||||
|
|
||||||
|
vsb.setBounds( r.x + (ltr ? padding : -padding), y, r.width, y2 - y );
|
||||||
|
|
||||||
|
// increase width of viewport, column header and horizontal scrollbar
|
||||||
|
if( canIncreaseViewportWidth( scrollPane ) ) {
|
||||||
|
int extraWidth = Math.min( padding, vsb.getWidth() );
|
||||||
|
resizeViewport( viewport, extraWidth, ltr );
|
||||||
|
resizeViewport( colHead, extraWidth, ltr );
|
||||||
|
resizeViewport( hsb, extraWidth, ltr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean canIncreaseViewportWidth( JScrollPane scrollPane ) {
|
||||||
|
return scrollPane.getComponentOrientation().isLeftToRight()
|
||||||
|
? !isCornerVisible( upperRight ) && !isCornerVisible( lowerRight )
|
||||||
|
: !isCornerVisible( upperLeft ) && !isCornerVisible( lowerLeft );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isCornerVisible( Component corner ) {
|
||||||
|
return corner != null &&
|
||||||
|
corner.getWidth() > 0 &&
|
||||||
|
corner.getHeight() > 0 &&
|
||||||
|
corner.isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void resizeViewport( Component c, int extraWidth, boolean ltr ) {
|
||||||
|
if( c == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Rectangle vr = c.getBounds();
|
||||||
|
c.setBounds( vr.x - (ltr ? 0 : extraWidth), vr.y, vr.width + extraWidth, vr.height );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import javax.swing.plaf.basic.BasicSeparatorUI;
|
|||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +135,7 @@ public class FlatSeparatorUI
|
|||||||
} else
|
} else
|
||||||
installStyle( s );
|
installStyle( s );
|
||||||
s.revalidate();
|
s.revalidate();
|
||||||
s.repaint();
|
HiDPIUtils.repaint( s );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
@@ -191,6 +193,23 @@ public class FlatSliderUI
|
|||||||
return new FlatTrackListener();
|
return new FlatTrackListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FocusListener createFocusListener( JSlider slider ) {
|
||||||
|
return new BasicSliderUI.FocusHandler() {
|
||||||
|
@Override
|
||||||
|
public void focusGained( FocusEvent e ) {
|
||||||
|
super.focusGained( e );
|
||||||
|
HiDPIUtils.repaint( slider );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost( FocusEvent e ) {
|
||||||
|
super.focusLost( e );
|
||||||
|
HiDPIUtils.repaint( slider );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
|
protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
|
||||||
return FlatStylingSupport.createPropertyChangeListener( slider, this::installStyle,
|
return FlatStylingSupport.createPropertyChangeListener( slider, this::installStyle,
|
||||||
@@ -422,7 +441,7 @@ debug*/
|
|||||||
Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, int focusWidth )
|
Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, int focusWidth )
|
||||||
{
|
{
|
||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
||||||
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
if( systemScaleFactor != (int) systemScaleFactor ) {
|
||||||
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
||||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
||||||
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||||
@@ -579,15 +598,15 @@ debug*/
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setThumbLocation( int x, int y ) {
|
public void setThumbLocation( int x, int y ) {
|
||||||
if( !isRoundThumb() ) {
|
|
||||||
// the needle of the directional thumb is painted outside of thumbRect
|
|
||||||
// --> must increase repaint rectangle
|
|
||||||
|
|
||||||
// set new thumb location and compute union of old and new thumb bounds
|
// set new thumb location and compute union of old and new thumb bounds
|
||||||
Rectangle r = new Rectangle( thumbRect );
|
Rectangle r = new Rectangle( thumbRect );
|
||||||
thumbRect.setLocation( x, y );
|
thumbRect.setLocation( x, y );
|
||||||
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
|
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
|
||||||
|
|
||||||
|
if( !isRoundThumb() ) {
|
||||||
|
// the needle of the directional thumb is painted outside of thumbRect
|
||||||
|
// --> must increase repaint rectangle
|
||||||
|
|
||||||
// increase union rectangle for repaint
|
// increase union rectangle for repaint
|
||||||
int extra = (int) Math.ceil( UIScale.scale( focusWidth ) * 0.4142f );
|
int extra = (int) Math.ceil( UIScale.scale( focusWidth ) * 0.4142f );
|
||||||
if( slider.getOrientation() == JSlider.HORIZONTAL )
|
if( slider.getOrientation() == JSlider.HORIZONTAL )
|
||||||
@@ -597,10 +616,9 @@ debug*/
|
|||||||
if( !slider.getComponentOrientation().isLeftToRight() )
|
if( !slider.getComponentOrientation().isLeftToRight() )
|
||||||
r.x -= extra;
|
r.x -= extra;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
slider.repaint( r );
|
HiDPIUtils.repaint( slider, r );
|
||||||
} else
|
|
||||||
super.setThumbLocation( x, y );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatTrackListener --------------------------------------------
|
//---- class FlatTrackListener --------------------------------------------
|
||||||
@@ -688,21 +706,21 @@ debug*/
|
|||||||
!UIManager.getBoolean( "Slider.snapToTicksOnReleased" ) )
|
!UIManager.getBoolean( "Slider.snapToTicksOnReleased" ) )
|
||||||
{
|
{
|
||||||
calculateThumbLocation();
|
calculateThumbLocation();
|
||||||
slider.repaint();
|
HiDPIUtils.repaint( slider );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setThumbHover( boolean hover ) {
|
protected void setThumbHover( boolean hover ) {
|
||||||
if( hover != thumbHover ) {
|
if( hover != thumbHover ) {
|
||||||
thumbHover = hover;
|
thumbHover = hover;
|
||||||
slider.repaint( thumbRect );
|
HiDPIUtils.repaint( slider, thumbRect );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setThumbPressed( boolean pressed ) {
|
protected void setThumbPressed( boolean pressed ) {
|
||||||
if( pressed != thumbPressed ) {
|
if( pressed != thumbPressed ) {
|
||||||
thumbPressed = pressed;
|
thumbPressed = pressed;
|
||||||
slider.repaint( thumbRect );
|
HiDPIUtils.repaint( slider, thumbRect );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import javax.swing.plaf.basic.BasicSpinnerUI;
|
|||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -586,7 +587,7 @@ public class FlatSpinnerUI
|
|||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
// necessary to update focus border
|
// necessary to update focus border
|
||||||
spinner.repaint();
|
HiDPIUtils.repaint( spinner );
|
||||||
|
|
||||||
// if spinner gained focus, transfer it to the editor text field
|
// if spinner gained focus, transfer it to the editor text field
|
||||||
if( e.getComponent() == spinner ) {
|
if( e.getComponent() == spinner ) {
|
||||||
@@ -599,7 +600,7 @@ public class FlatSpinnerUI
|
|||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
// necessary to update focus border
|
// necessary to update focus border
|
||||||
spinner.repaint();
|
HiDPIUtils.repaint( spinner );
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- interface PropertyChangeListener ----
|
//---- interface PropertyChangeListener ----
|
||||||
@@ -614,7 +615,7 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
||||||
case FlatClientProperties.OUTLINE:
|
case FlatClientProperties.OUTLINE:
|
||||||
spinner.repaint();
|
HiDPIUtils.repaint( spinner );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.MINIMUM_WIDTH:
|
case FlatClientProperties.MINIMUM_WIDTH:
|
||||||
@@ -625,7 +626,7 @@ public class FlatSpinnerUI
|
|||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
spinner.revalidate();
|
spinner.revalidate();
|
||||||
spinner.repaint();
|
HiDPIUtils.repaint( spinner );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Canvas;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
@@ -67,6 +69,8 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* <!-- FlatSplitPaneUI -->
|
* <!-- FlatSplitPaneUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
|
* @uiDefault SplitPaneDivider.hoverColor Color optional
|
||||||
|
* @uiDefault SplitPaneDivider.pressedColor Color optional
|
||||||
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color
|
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color
|
||||||
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
|
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
|
||||||
* @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color
|
* @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color
|
||||||
@@ -80,9 +84,10 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatSplitPaneUI
|
public class FlatSplitPaneUI
|
||||||
extends BasicSplitPaneUI
|
extends BasicSplitPaneUI
|
||||||
implements StyleableUI
|
implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
|
||||||
{
|
{
|
||||||
@Styleable protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
|
/** @since 3.3 */ @Styleable protected Color draggingColor;
|
||||||
@Styleable protected Color oneTouchArrowColor;
|
@Styleable protected Color oneTouchArrowColor;
|
||||||
@Styleable protected Color oneTouchHoverArrowColor;
|
@Styleable protected Color oneTouchHoverArrowColor;
|
||||||
@Styleable protected Color oneTouchPressedArrowColor;
|
@Styleable protected Color oneTouchPressedArrowColor;
|
||||||
@@ -104,6 +109,8 @@ public class FlatSplitPaneUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
|
|
||||||
|
draggingColor = UIManager.getColor( "SplitPaneDivider.draggingColor" );
|
||||||
|
|
||||||
// get one-touch colors before invoking super.installDefaults() because they are
|
// get one-touch colors before invoking super.installDefaults() because they are
|
||||||
// used in there on LaF switching
|
// used in there on LaF switching
|
||||||
oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" );
|
oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" );
|
||||||
@@ -117,6 +124,8 @@ public class FlatSplitPaneUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
draggingColor = null;
|
||||||
|
|
||||||
oneTouchArrowColor = null;
|
oneTouchArrowColor = null;
|
||||||
oneTouchHoverArrowColor = null;
|
oneTouchHoverArrowColor = null;
|
||||||
oneTouchPressedArrowColor = null;
|
oneTouchPressedArrowColor = null;
|
||||||
@@ -183,12 +192,58 @@ public class FlatSplitPaneUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Component createDefaultNonContinuousLayoutDivider() {
|
||||||
|
// only used for non-continuous layout if left or right component is heavy weight
|
||||||
|
return new Canvas() {
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g ) {
|
||||||
|
if( !isContinuousLayout() && getLastDragLocation() != -1 )
|
||||||
|
paintDragDivider( g, 0 );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishedPaintingChildren( JSplitPane sp, Graphics g ) {
|
||||||
|
if( sp == splitPane && getLastDragLocation() != -1 && !isContinuousLayout() && !draggingHW )
|
||||||
|
paintDragDivider( g, getLastDragLocation() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void paintDragDivider( Graphics g, int dividerLocation ) {
|
||||||
|
// divider bounds
|
||||||
|
boolean horizontal = (getOrientation() == JSplitPane.HORIZONTAL_SPLIT);
|
||||||
|
int x = horizontal ? dividerLocation : 0;
|
||||||
|
int y = !horizontal ? dividerLocation : 0;
|
||||||
|
int width = horizontal ? dividerSize : splitPane.getWidth();
|
||||||
|
int height = !horizontal ? dividerSize : splitPane.getHeight();
|
||||||
|
|
||||||
|
// paint background
|
||||||
|
g.setColor( FlatUIUtils.deriveColor( draggingColor, splitPane.getBackground() ) );
|
||||||
|
g.fillRect( x, y, width, height );
|
||||||
|
|
||||||
|
// paint divider style (e.g. grip)
|
||||||
|
if( divider instanceof FlatSplitPaneDivider )
|
||||||
|
((FlatSplitPaneDivider)divider).paintStyle( g, x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
|
||||||
|
|
||||||
|
/** @since 3.4 */
|
||||||
|
@Override
|
||||||
|
public Boolean isTitleBarCaptionAt( int x, int y ) {
|
||||||
|
// necessary because BasicSplitPaneDivider adds some mouse listeners for dragging divider
|
||||||
|
return null; // check children
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatSplitPaneDivider -----------------------------------------
|
//---- class FlatSplitPaneDivider -----------------------------------------
|
||||||
|
|
||||||
protected class FlatSplitPaneDivider
|
protected class FlatSplitPaneDivider
|
||||||
extends BasicSplitPaneDivider
|
extends BasicSplitPaneDivider
|
||||||
{
|
{
|
||||||
@Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" );
|
@Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" );
|
||||||
|
/** @since 3.3 */ @Styleable protected Color hoverColor = UIManager.getColor( "SplitPaneDivider.hoverColor" );
|
||||||
|
/** @since 3.3 */ @Styleable protected Color pressedColor = UIManager.getColor( "SplitPaneDivider.pressedColor" );
|
||||||
@Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
|
@Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
|
||||||
@Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
|
@Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
|
||||||
@Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
|
@Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
|
||||||
@@ -246,20 +301,40 @@ public class FlatSplitPaneUI
|
|||||||
// necessary to show/hide one-touch buttons on expand/collapse
|
// necessary to show/hide one-touch buttons on expand/collapse
|
||||||
doLayout();
|
doLayout();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE:
|
||||||
|
revalidate();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g ) {
|
public void paint( Graphics g ) {
|
||||||
|
// paint hover or pressed background
|
||||||
|
Color hoverOrPressedColor = (isContinuousLayout() && dragger != null)
|
||||||
|
? pressedColor
|
||||||
|
: (isMouseOver() && dragger == null
|
||||||
|
? hoverColor
|
||||||
|
: null);
|
||||||
|
if( hoverOrPressedColor != null ) {
|
||||||
|
g.setColor( FlatUIUtils.deriveColor( hoverOrPressedColor, splitPane.getBackground() ) );
|
||||||
|
g.fillRect( 0, 0, getWidth(), getHeight() );
|
||||||
|
}
|
||||||
|
|
||||||
super.paint( g );
|
super.paint( g );
|
||||||
|
|
||||||
|
paintStyle( g, 0, 0, getWidth(), getHeight() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.3 */
|
||||||
|
protected void paintStyle( Graphics g, int x, int y, int width, int height ) {
|
||||||
if( "plain".equals( style ) )
|
if( "plain".equals( style ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
|
||||||
g.setColor( gripColor );
|
g.setColor( gripColor );
|
||||||
paintGrip( g, 0, 0, getWidth(), getHeight() );
|
paintGrip( g, x, y, width, height );
|
||||||
|
|
||||||
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
}
|
}
|
||||||
@@ -286,6 +361,29 @@ public class FlatSplitPaneUI
|
|||||||
: location == (splitPane.getWidth() - getWidth() - insets.right);
|
: location == (splitPane.getWidth() - getWidth() - insets.right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setMouseOver( boolean mouseOver ) {
|
||||||
|
super.setMouseOver( mouseOver );
|
||||||
|
repaintIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void prepareForDragging() {
|
||||||
|
super.prepareForDragging();
|
||||||
|
repaintIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finishDraggingTo( int location ) {
|
||||||
|
super.finishDraggingTo( location );
|
||||||
|
repaintIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintIfNecessary() {
|
||||||
|
if( hoverColor != null || pressedColor != null )
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatOneTouchButton ---------------------------------------
|
//---- class FlatOneTouchButton ---------------------------------------
|
||||||
|
|
||||||
protected class FlatOneTouchButton
|
protected class FlatOneTouchButton
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
@@ -709,7 +710,7 @@ public class FlatStylingSupport
|
|||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle.run();
|
installStyle.run();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -65,8 +65,28 @@ public class FlatTableCellBorder
|
|||||||
return super.getLineColor();
|
return super.getLineColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getArc() {
|
||||||
|
if( c != null ) {
|
||||||
|
Integer selectionArc = getStyleFromTableUI( c, ui -> ui.selectionArc );
|
||||||
|
if( selectionArc != null )
|
||||||
|
return selectionArc;
|
||||||
|
}
|
||||||
|
return super.getArc();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
if( c != null ) {
|
||||||
|
Insets selectionInsets = getStyleFromTableUI( c, ui -> ui.selectionInsets );
|
||||||
|
if( selectionInsets != null ) {
|
||||||
|
x += selectionInsets.left;
|
||||||
|
y += selectionInsets.top;
|
||||||
|
width -= selectionInsets.left + selectionInsets.right;
|
||||||
|
height -= selectionInsets.top + selectionInsets.bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.c = c;
|
this.c = c;
|
||||||
super.paintBorder( c, g, x, y, width, height );
|
super.paintBorder( c, g, x, y, width, height );
|
||||||
this.c = null;
|
this.c = null;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
@@ -28,16 +29,15 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.swing.CellRendererPane;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
|
||||||
import javax.swing.event.MouseInputListener;
|
import javax.swing.event.MouseInputListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
|
||||||
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
||||||
import javax.swing.table.JTableHeader;
|
import javax.swing.table.JTableHeader;
|
||||||
import javax.swing.table.TableCellRenderer;
|
import javax.swing.table.TableCellRenderer;
|
||||||
@@ -45,6 +45,7 @@ import javax.swing.table.TableColumn;
|
|||||||
import javax.swing.table.TableColumnModel;
|
import javax.swing.table.TableColumnModel;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -114,6 +115,11 @@ public class FlatTableHeaderUI
|
|||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
|
// replace cell renderer pane
|
||||||
|
header.remove( rendererPane );
|
||||||
|
rendererPane = new FlatTableHeaderCellRendererPane();
|
||||||
|
header.add( rendererPane );
|
||||||
|
|
||||||
installStyle();
|
installStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,8 +235,8 @@ public class FlatTableHeaderUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void rolloverColumnUpdated( int oldColumn, int newColumn ) {
|
protected void rolloverColumnUpdated( int oldColumn, int newColumn ) {
|
||||||
header.repaint( header.getHeaderRect( oldColumn ) );
|
HiDPIUtils.repaint( header, header.getHeaderRect( oldColumn ) );
|
||||||
header.repaint( header.getHeaderRect( newColumn ) );
|
HiDPIUtils.repaint( header, header.getHeaderRect( newColumn ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -265,16 +271,8 @@ public class FlatTableHeaderUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporary use own default renderer
|
|
||||||
FlatTableCellHeaderRenderer tempRenderer = new FlatTableCellHeaderRenderer( header.getDefaultRenderer() );
|
|
||||||
header.setDefaultRenderer( tempRenderer );
|
|
||||||
|
|
||||||
// paint header
|
// paint header
|
||||||
super.paint( g, c );
|
super.paint( g, c );
|
||||||
|
|
||||||
// restore default renderer
|
|
||||||
tempRenderer.reset();
|
|
||||||
header.setDefaultRenderer( tempRenderer.delegate );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSystemDefaultRenderer( Object headerRenderer ) {
|
private boolean isSystemDefaultRenderer( Object headerRenderer ) {
|
||||||
@@ -332,119 +330,129 @@ public class FlatTableHeaderUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatTableCellHeaderRenderer ----------------------------------
|
//---- class FlatTableHeaderCellRendererPane ------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A delegating header renderer that is only used to paint hover and pressed
|
* Cell renderer pane that is used to paint hover and pressed background/foreground
|
||||||
* background/foreground and to paint sort arrows at top, bottom or left position.
|
* and to paint sort arrows at top, bottom or left position.
|
||||||
*/
|
*/
|
||||||
private class FlatTableCellHeaderRenderer
|
private class FlatTableHeaderCellRendererPane
|
||||||
implements TableCellRenderer, Border, UIResource
|
extends CellRendererPane
|
||||||
{
|
{
|
||||||
private final TableCellRenderer delegate;
|
private final Icon ascendingSortIcon;
|
||||||
|
private final Icon descendingSortIcon;
|
||||||
|
|
||||||
private JLabel l;
|
public FlatTableHeaderCellRendererPane() {
|
||||||
private Color oldBackground;
|
ascendingSortIcon = UIManager.getIcon( "Table.ascendingSortIcon" );
|
||||||
private Color oldForeground;
|
descendingSortIcon = UIManager.getIcon( "Table.descendingSortIcon" );
|
||||||
private Boolean oldOpaque;
|
|
||||||
private int oldHorizontalTextPosition = -1;
|
|
||||||
private Border origBorder;
|
|
||||||
private Icon sortIcon;
|
|
||||||
|
|
||||||
FlatTableCellHeaderRenderer( TableCellRenderer delegate ) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected,
|
public void paintComponent( Graphics g, Component c, Container p, int x, int y, int w, int h, boolean shouldValidate ) {
|
||||||
boolean hasFocus, int row, int column )
|
if( !(c instanceof JLabel) ) {
|
||||||
{
|
super.paintComponent( g, c, p, x, y, w, h, shouldValidate );
|
||||||
Component c = delegate.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column );
|
return;
|
||||||
if( !(c instanceof JLabel) )
|
}
|
||||||
return c;
|
|
||||||
|
|
||||||
l = (JLabel) c;
|
JLabel l = (JLabel) c;
|
||||||
|
Color oldBackground = null;
|
||||||
|
Color oldForeground = null;
|
||||||
|
boolean oldOpaque = false;
|
||||||
|
Icon oldIcon = null;
|
||||||
|
int oldHorizontalTextPosition = -1;
|
||||||
|
|
||||||
// hover and pressed background/foreground
|
// hover and pressed background/foreground
|
||||||
TableColumn draggedColumn = header.getDraggedColumn();
|
TableColumn draggedColumn = header.getDraggedColumn();
|
||||||
Color background = null;
|
Color background = null;
|
||||||
Color foreground = null;
|
Color foreground = null;
|
||||||
if( draggedColumn != null && header.getTable().convertColumnIndexToView( draggedColumn.getModelIndex() ) == column ) {
|
if( draggedColumn != null &&
|
||||||
|
header.getTable().convertColumnIndexToView( draggedColumn.getModelIndex() )
|
||||||
|
== getColumn( x - header.getDraggedDistance(), w ) )
|
||||||
|
{
|
||||||
background = pressedBackground;
|
background = pressedBackground;
|
||||||
foreground = pressedForeground;
|
foreground = pressedForeground;
|
||||||
} else if( getRolloverColumn() == column ) {
|
} else if( getRolloverColumn() >= 0 && getRolloverColumn() == getColumn( x, w ) ) {
|
||||||
background = hoverBackground;
|
background = hoverBackground;
|
||||||
foreground = hoverForeground;
|
foreground = hoverForeground;
|
||||||
}
|
}
|
||||||
if( background != null ) {
|
if( background != null ) {
|
||||||
if( oldBackground == null )
|
|
||||||
oldBackground = l.getBackground();
|
oldBackground = l.getBackground();
|
||||||
if( oldOpaque == null )
|
|
||||||
oldOpaque = l.isOpaque();
|
oldOpaque = l.isOpaque();
|
||||||
l.setBackground( FlatUIUtils.deriveColor( background, header.getBackground() ) );
|
l.setBackground( FlatUIUtils.deriveColor( background, header.getBackground() ) );
|
||||||
l.setOpaque( true );
|
l.setOpaque( true );
|
||||||
}
|
}
|
||||||
if( foreground != null ) {
|
if( foreground != null ) {
|
||||||
if( oldForeground == null )
|
|
||||||
oldForeground = l.getForeground();
|
oldForeground = l.getForeground();
|
||||||
l.setForeground( FlatUIUtils.deriveColor( foreground, header.getForeground() ) );
|
l.setForeground( FlatUIUtils.deriveColor( foreground, header.getForeground() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort icon
|
// sort icon position
|
||||||
|
Icon icon = l.getIcon();
|
||||||
|
boolean isSortIcon = (icon != null && (icon == ascendingSortIcon || icon == descendingSortIcon));
|
||||||
|
if( isSortIcon ) {
|
||||||
if( sortIconPosition == SwingConstants.LEFT ) {
|
if( sortIconPosition == SwingConstants.LEFT ) {
|
||||||
// left
|
// left
|
||||||
if( oldHorizontalTextPosition < 0 )
|
|
||||||
oldHorizontalTextPosition = l.getHorizontalTextPosition();
|
oldHorizontalTextPosition = l.getHorizontalTextPosition();
|
||||||
l.setHorizontalTextPosition( SwingConstants.RIGHT );
|
l.setHorizontalTextPosition( SwingConstants.RIGHT );
|
||||||
} else if( sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM ) {
|
} else if( sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM ) {
|
||||||
// top or bottom
|
// top or bottom
|
||||||
sortIcon = l.getIcon();
|
oldIcon = icon;
|
||||||
origBorder = l.getBorder();
|
|
||||||
l.setIcon( null );
|
l.setIcon( null );
|
||||||
l.setBorder( this );
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return l;
|
// paint renderer component
|
||||||
|
super.paintComponent( g, c, p, x, y, w, h, shouldValidate );
|
||||||
|
|
||||||
|
// paint top or bottom sort icon
|
||||||
|
if( isSortIcon && (sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM) ) {
|
||||||
|
int xi = x + ((w - icon.getIconWidth()) / 2);
|
||||||
|
int yi = (sortIconPosition == SwingConstants.TOP)
|
||||||
|
? y + UIScale.scale( 1 )
|
||||||
|
: y + height - icon.getIconHeight()
|
||||||
|
- 1 // for gap
|
||||||
|
- (int) (1 * UIScale.getUserScaleFactor()); // for bottom border
|
||||||
|
icon.paintIcon( c, g, xi, yi );
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
// restore modified renderer component properties
|
||||||
if( l == null )
|
if( background != null ) {
|
||||||
return;
|
|
||||||
|
|
||||||
if( oldBackground != null )
|
|
||||||
l.setBackground( oldBackground );
|
l.setBackground( oldBackground );
|
||||||
if( oldForeground != null )
|
|
||||||
l.setForeground( oldForeground );
|
|
||||||
if( oldOpaque != null )
|
|
||||||
l.setOpaque( oldOpaque );
|
l.setOpaque( oldOpaque );
|
||||||
|
}
|
||||||
|
if( foreground != null )
|
||||||
|
l.setForeground( oldForeground );
|
||||||
|
if( oldIcon != null )
|
||||||
|
l.setIcon( oldIcon );
|
||||||
if( oldHorizontalTextPosition >= 0 )
|
if( oldHorizontalTextPosition >= 0 )
|
||||||
l.setHorizontalTextPosition( oldHorizontalTextPosition );
|
l.setHorizontalTextPosition( oldHorizontalTextPosition );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
* Get column index for given coordinates.
|
||||||
if( origBorder != null )
|
*/
|
||||||
origBorder.paintBorder( c, g, x, y, width, height );
|
private int getColumn( int x, int width ) {
|
||||||
|
TableColumnModel columnModel = header.getColumnModel();
|
||||||
|
int columnCount = columnModel.getColumnCount();
|
||||||
|
boolean ltr = header.getComponentOrientation().isLeftToRight();
|
||||||
|
int cx = ltr ? 0 : getWidthInRightToLef();
|
||||||
|
|
||||||
if( sortIcon != null ) {
|
for( int i = 0; i < columnCount; i++ ) {
|
||||||
int xi = x + ((width - sortIcon.getIconWidth()) / 2);
|
int cw = columnModel.getColumn( i ).getWidth();
|
||||||
int yi = (sortIconPosition == SwingConstants.TOP)
|
if( x == cx - (ltr ? 0 : cw) && width == cw )
|
||||||
? y + UIScale.scale( 1 )
|
return i;
|
||||||
: y + height - sortIcon.getIconHeight()
|
|
||||||
- 1 // for gap
|
cx += ltr ? cw : -cw;
|
||||||
- (int) (1 * UIScale.getUserScaleFactor()); // for bottom border
|
|
||||||
sortIcon.paintIcon( c, g, xi, yi );
|
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// similar to JTableHeader.getWidthInRightToLeft()
|
||||||
public Insets getBorderInsets( Component c ) {
|
private int getWidthInRightToLef() {
|
||||||
return (origBorder != null) ? origBorder.getBorderInsets( c ) : new Insets( 0, 0, 0, 0 );
|
JTable table = header.getTable();
|
||||||
}
|
return (table != null && table.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF)
|
||||||
|
? table.getWidth()
|
||||||
@Override
|
: header.getWidth();
|
||||||
public boolean isBorderOpaque() {
|
|
||||||
return (origBorder != null) ? origBorder.isBorderOpaque() : false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,30 +17,56 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Shape;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ComponentAdapter;
|
||||||
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.ComponentListener;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.JTextField;
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
|
import javax.swing.ListSelectionModel;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ListSelectionEvent;
|
||||||
|
import javax.swing.event.ListSelectionListener;
|
||||||
|
import javax.swing.event.TableColumnModelEvent;
|
||||||
|
import javax.swing.event.TableColumnModelListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicTableUI;
|
import javax.swing.plaf.basic.BasicTableUI;
|
||||||
|
import javax.swing.table.DefaultTableCellRenderer;
|
||||||
import javax.swing.table.JTableHeader;
|
import javax.swing.table.JTableHeader;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
|
import javax.swing.table.TableColumnModel;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -80,7 +106,10 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Table.intercellSpacing Dimension
|
* @uiDefault Table.intercellSpacing Dimension
|
||||||
* @uiDefault Table.selectionInactiveBackground Color
|
* @uiDefault Table.selectionInactiveBackground Color
|
||||||
* @uiDefault Table.selectionInactiveForeground Color
|
* @uiDefault Table.selectionInactiveForeground Color
|
||||||
|
* @uiDefault Table.selectionInsets Insets
|
||||||
|
* @uiDefault Table.selectionArc int
|
||||||
* @uiDefault Table.paintOutsideAlternateRows boolean
|
* @uiDefault Table.paintOutsideAlternateRows boolean
|
||||||
|
* @uiDefault Table.editorSelectAllOnStartEditing boolean
|
||||||
*
|
*
|
||||||
* <!-- FlatTableCellBorder -->
|
* <!-- FlatTableCellBorder -->
|
||||||
*
|
*
|
||||||
@@ -107,6 +136,8 @@ public class FlatTableUI
|
|||||||
@Styleable protected Color selectionForeground;
|
@Styleable protected Color selectionForeground;
|
||||||
@Styleable protected Color selectionInactiveBackground;
|
@Styleable protected Color selectionInactiveBackground;
|
||||||
@Styleable protected Color selectionInactiveForeground;
|
@Styleable protected Color selectionInactiveForeground;
|
||||||
|
/** @since 3.5 */ @Styleable protected Insets selectionInsets;
|
||||||
|
/** @since 3.5 */ @Styleable protected int selectionArc;
|
||||||
|
|
||||||
// for FlatTableCellBorder
|
// for FlatTableCellBorder
|
||||||
/** @since 2 */ @Styleable protected Insets cellMargins;
|
/** @since 2 */ @Styleable protected Insets cellMargins;
|
||||||
@@ -116,8 +147,12 @@ public class FlatTableUI
|
|||||||
private boolean oldShowHorizontalLines;
|
private boolean oldShowHorizontalLines;
|
||||||
private boolean oldShowVerticalLines;
|
private boolean oldShowVerticalLines;
|
||||||
private Dimension oldIntercellSpacing;
|
private Dimension oldIntercellSpacing;
|
||||||
|
private TableCellRenderer oldBooleanRenderer;
|
||||||
|
|
||||||
private PropertyChangeListener propertyChangeListener;
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
private ComponentListener outsideAlternateRowsListener;
|
||||||
|
private ListSelectionListener rowSelectionListener;
|
||||||
|
private TableColumnModelListener columnSelectionListener;
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
@@ -144,6 +179,8 @@ public class FlatTableUI
|
|||||||
selectionForeground = UIManager.getColor( "Table.selectionForeground" );
|
selectionForeground = UIManager.getColor( "Table.selectionForeground" );
|
||||||
selectionInactiveBackground = UIManager.getColor( "Table.selectionInactiveBackground" );
|
selectionInactiveBackground = UIManager.getColor( "Table.selectionInactiveBackground" );
|
||||||
selectionInactiveForeground = UIManager.getColor( "Table.selectionInactiveForeground" );
|
selectionInactiveForeground = UIManager.getColor( "Table.selectionInactiveForeground" );
|
||||||
|
selectionInsets = UIManager.getInsets( "Table.selectionInsets" );
|
||||||
|
selectionArc = UIManager.getInt( "Table.selectionArc" );
|
||||||
|
|
||||||
toggleSelectionColors();
|
toggleSelectionColors();
|
||||||
|
|
||||||
@@ -151,19 +188,35 @@ public class FlatTableUI
|
|||||||
if( rowHeight > 0 )
|
if( rowHeight > 0 )
|
||||||
LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) );
|
LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) );
|
||||||
|
|
||||||
if( !showHorizontalLines ) {
|
FlatTablePropertyWatcher watcher = FlatTablePropertyWatcher.get( table );
|
||||||
|
if( watcher != null )
|
||||||
|
watcher.enabled = false;
|
||||||
|
|
||||||
|
if( !showHorizontalLines && (watcher == null || !watcher.showHorizontalLinesChanged) ) {
|
||||||
oldShowHorizontalLines = table.getShowHorizontalLines();
|
oldShowHorizontalLines = table.getShowHorizontalLines();
|
||||||
table.setShowHorizontalLines( false );
|
table.setShowHorizontalLines( false );
|
||||||
}
|
}
|
||||||
if( !showVerticalLines ) {
|
if( !showVerticalLines && (watcher == null || !watcher.showVerticalLinesChanged) ) {
|
||||||
oldShowVerticalLines = table.getShowVerticalLines();
|
oldShowVerticalLines = table.getShowVerticalLines();
|
||||||
table.setShowVerticalLines( false );
|
table.setShowVerticalLines( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( intercellSpacing != null ) {
|
if( intercellSpacing != null && (watcher == null || !watcher.intercellSpacingChanged) ) {
|
||||||
oldIntercellSpacing = table.getIntercellSpacing();
|
oldIntercellSpacing = table.getIntercellSpacing();
|
||||||
table.setIntercellSpacing( intercellSpacing );
|
table.setIntercellSpacing( intercellSpacing );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( watcher != null )
|
||||||
|
watcher.enabled = true;
|
||||||
|
else
|
||||||
|
table.addPropertyChangeListener( new FlatTablePropertyWatcher() );
|
||||||
|
|
||||||
|
// install boolean renderer
|
||||||
|
oldBooleanRenderer = table.getDefaultRenderer( Boolean.class );
|
||||||
|
if( oldBooleanRenderer instanceof UIResource )
|
||||||
|
table.setDefaultRenderer( Boolean.class, new FlatBooleanRenderer() );
|
||||||
|
else
|
||||||
|
oldBooleanRenderer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -177,15 +230,36 @@ public class FlatTableUI
|
|||||||
|
|
||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
|
|
||||||
|
FlatTablePropertyWatcher watcher = FlatTablePropertyWatcher.get( table );
|
||||||
|
if( watcher != null )
|
||||||
|
watcher.enabled = false;
|
||||||
|
|
||||||
// restore old show horizontal/vertical lines (if not modified)
|
// restore old show horizontal/vertical lines (if not modified)
|
||||||
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() )
|
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() &&
|
||||||
|
(watcher == null || !watcher.showHorizontalLinesChanged) )
|
||||||
table.setShowHorizontalLines( true );
|
table.setShowHorizontalLines( true );
|
||||||
if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() )
|
if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() &&
|
||||||
|
(watcher == null || !watcher.showVerticalLinesChanged) )
|
||||||
table.setShowVerticalLines( true );
|
table.setShowVerticalLines( true );
|
||||||
|
|
||||||
// restore old intercell spacing (if not modified)
|
// restore old intercell spacing (if not modified)
|
||||||
if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) )
|
if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) &&
|
||||||
|
(watcher == null || !watcher.intercellSpacingChanged) )
|
||||||
table.setIntercellSpacing( oldIntercellSpacing );
|
table.setIntercellSpacing( oldIntercellSpacing );
|
||||||
|
|
||||||
|
if( watcher != null )
|
||||||
|
watcher.enabled = true;
|
||||||
|
|
||||||
|
// uninstall boolean renderer
|
||||||
|
if( table.getDefaultRenderer( Boolean.class ) instanceof FlatBooleanRenderer ) {
|
||||||
|
if( oldBooleanRenderer instanceof Component ) {
|
||||||
|
// because the old renderer component was not attached to any component hierarchy,
|
||||||
|
// its UI was not yet updated, and it is necessary to do it here
|
||||||
|
SwingUtilities.updateComponentTreeUI( (Component) oldBooleanRenderer );
|
||||||
|
}
|
||||||
|
table.setDefaultRenderer( Boolean.class, oldBooleanRenderer );
|
||||||
|
}
|
||||||
|
oldBooleanRenderer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -194,6 +268,28 @@ public class FlatTableUI
|
|||||||
|
|
||||||
propertyChangeListener = e -> {
|
propertyChangeListener = e -> {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
|
case "selectionModel":
|
||||||
|
if( rowSelectionListener != null ) {
|
||||||
|
Object oldModel = e.getOldValue();
|
||||||
|
Object newModel = e.getNewValue();
|
||||||
|
if( oldModel != null )
|
||||||
|
((ListSelectionModel)oldModel).removeListSelectionListener( rowSelectionListener );
|
||||||
|
if( newModel != null )
|
||||||
|
((ListSelectionModel)newModel).addListSelectionListener( rowSelectionListener );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "columnModel":
|
||||||
|
if( columnSelectionListener != null ) {
|
||||||
|
Object oldModel = e.getOldValue();
|
||||||
|
Object newModel = e.getNewValue();
|
||||||
|
if( oldModel != null )
|
||||||
|
((TableColumnModel)oldModel).removeColumnModelListener( columnSelectionListener );
|
||||||
|
if( newModel != null )
|
||||||
|
((TableColumnModel)newModel).addColumnModelListener( columnSelectionListener );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.COMPONENT_FOCUS_OWNER:
|
case FlatClientProperties.COMPONENT_FOCUS_OWNER:
|
||||||
toggleSelectionColors();
|
toggleSelectionColors();
|
||||||
break;
|
break;
|
||||||
@@ -202,11 +298,14 @@ public class FlatTableUI
|
|||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
table.revalidate();
|
table.revalidate();
|
||||||
table.repaint();
|
HiDPIUtils.repaint( table );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
table.addPropertyChangeListener( propertyChangeListener );
|
table.addPropertyChangeListener( propertyChangeListener );
|
||||||
|
|
||||||
|
if( selectionArc > 0 )
|
||||||
|
installRepaintRoundedSelectionListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -215,6 +314,19 @@ public class FlatTableUI
|
|||||||
|
|
||||||
table.removePropertyChangeListener( propertyChangeListener );
|
table.removePropertyChangeListener( propertyChangeListener );
|
||||||
propertyChangeListener = null;
|
propertyChangeListener = null;
|
||||||
|
|
||||||
|
if( outsideAlternateRowsListener != null ) {
|
||||||
|
table.removeComponentListener( outsideAlternateRowsListener );
|
||||||
|
outsideAlternateRowsListener = null;
|
||||||
|
}
|
||||||
|
if( rowSelectionListener != null ) {
|
||||||
|
table.getSelectionModel().removeListSelectionListener( rowSelectionListener );
|
||||||
|
rowSelectionListener = null;
|
||||||
|
}
|
||||||
|
if( columnSelectionListener != null ) {
|
||||||
|
table.getColumnModel().removeColumnModelListener( columnSelectionListener );
|
||||||
|
columnSelectionListener = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -238,6 +350,18 @@ public class FlatTableUI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installKeyboardActions() {
|
||||||
|
super.installKeyboardActions();
|
||||||
|
|
||||||
|
if( UIManager.getBoolean( "Table.editorSelectAllOnStartEditing" ) ) {
|
||||||
|
// get shared action map, used for all tables
|
||||||
|
ActionMap map = SwingUtilities.getUIActionMap( table );
|
||||||
|
if( map != null )
|
||||||
|
StartEditingAction.install( map, "startEditing" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected void installStyle() {
|
protected void installStyle() {
|
||||||
try {
|
try {
|
||||||
@@ -279,6 +403,8 @@ public class FlatTableUI
|
|||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
if( "rowHeight".equals( key ) && value instanceof Integer )
|
if( "rowHeight".equals( key ) && value instanceof Integer )
|
||||||
value = UIScale.scale( (Integer) value );
|
value = UIScale.scale( (Integer) value );
|
||||||
|
else if( "selectionArc".equals( key ) && value instanceof Integer && (Integer) value > 0 )
|
||||||
|
installRepaintRoundedSelectionListeners();
|
||||||
|
|
||||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, table, key, value );
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, table, key, value );
|
||||||
}
|
}
|
||||||
@@ -341,6 +467,7 @@ public class FlatTableUI
|
|||||||
|
|
||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
||||||
double lineThickness = (1. / systemScaleFactor) * (int) systemScaleFactor;
|
double lineThickness = (1. / systemScaleFactor) * (int) systemScaleFactor;
|
||||||
|
double lineOffset = (1. - lineThickness) + 0.05; // adding 0.05 to fix line location in some cases
|
||||||
|
|
||||||
// Java 8 uses drawLine() to paint grid lines
|
// Java 8 uses drawLine() to paint grid lines
|
||||||
// Java 9+ uses fillRect() to paint grid lines (except for dragged column)
|
// Java 9+ uses fillRect() to paint grid lines (except for dragged column)
|
||||||
@@ -383,11 +510,11 @@ public class FlatTableUI
|
|||||||
// reduce line thickness to avoid unstable painted line thickness
|
// reduce line thickness to avoid unstable painted line thickness
|
||||||
if( lineThickness != 1 ) {
|
if( lineThickness != 1 ) {
|
||||||
if( horizontalLines && height == 1 && wasInvokedFromPaintGrid() ) {
|
if( horizontalLines && height == 1 && wasInvokedFromPaintGrid() ) {
|
||||||
super.fill( new Rectangle2D.Double( x, y, width, lineThickness ) );
|
super.fill( new Rectangle2D.Double( x, y + lineOffset, width, lineThickness ) );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if( verticalLines && width == 1 && y == 0 && wasInvokedFromPaintGrid() ) {
|
if( verticalLines && width == 1 && y == 0 && wasInvokedFromPaintGrid() ) {
|
||||||
super.fill( new Rectangle2D.Double( x, y, lineThickness, height ) );
|
super.fill( new Rectangle2D.Double( x + lineOffset, y, lineThickness, height ) );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,6 +532,10 @@ public class FlatTableUI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rounded selection or selection insets
|
||||||
|
if( selectionArc > 0 || (selectionInsets != null && !FlatUIUtils.isInsetsEmpty( selectionInsets )) )
|
||||||
|
g = new RoundedSelectionGraphics( g, UIManager.getColor( "Table.alternateRowColor" ) );
|
||||||
|
|
||||||
super.paint( g, c );
|
super.paint( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,8 +581,6 @@ public class FlatTableUI
|
|||||||
boolean paintOutside = UIManager.getBoolean( "Table.paintOutsideAlternateRows" );
|
boolean paintOutside = UIManager.getBoolean( "Table.paintOutsideAlternateRows" );
|
||||||
Color alternateColor;
|
Color alternateColor;
|
||||||
if( paintOutside && (alternateColor = UIManager.getColor( "Table.alternateRowColor" )) != null ) {
|
if( paintOutside && (alternateColor = UIManager.getColor( "Table.alternateRowColor" )) != null ) {
|
||||||
g.setColor( alternateColor );
|
|
||||||
|
|
||||||
int rowCount = table.getRowCount();
|
int rowCount = table.getRowCount();
|
||||||
|
|
||||||
// paint alternating empty rows below the table
|
// paint alternating empty rows below the table
|
||||||
@@ -460,11 +589,449 @@ public class FlatTableUI
|
|||||||
int tableWidth = table.getWidth();
|
int tableWidth = table.getWidth();
|
||||||
int rowHeight = table.getRowHeight();
|
int rowHeight = table.getRowHeight();
|
||||||
|
|
||||||
|
g.setColor( alternateColor );
|
||||||
|
|
||||||
|
int x = viewport.getComponentOrientation().isLeftToRight() ? 0 : viewportWidth - tableWidth;
|
||||||
for( int y = tableHeight, row = rowCount; y < viewportHeight; y += rowHeight, row++ ) {
|
for( int y = tableHeight, row = rowCount; y < viewportHeight; y += rowHeight, row++ ) {
|
||||||
if( row % 2 != 0 )
|
if( row % 2 != 0 )
|
||||||
g.fillRect( 0, y, tableWidth, rowHeight );
|
paintAlternateRowBackground( g, -1, -1, x, y, tableWidth, rowHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
// add listener on demand
|
||||||
|
if( outsideAlternateRowsListener == null && table.getAutoResizeMode() == JTable.AUTO_RESIZE_OFF ) {
|
||||||
|
outsideAlternateRowsListener = new FlatOutsideAlternateRowsListener();
|
||||||
|
table.addComponentListener( outsideAlternateRowsListener );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paints (rounded) alternate row background.
|
||||||
|
* Supports {@link #selectionArc} and {@link #selectionInsets}.
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> This method is only invoked if either selection arc
|
||||||
|
* is greater than zero or if selection insets are not empty.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
protected void paintAlternateRowBackground( Graphics g, int row, int column, int x, int y, int width, int height ) {
|
||||||
|
Insets insets = (selectionInsets != null) ? (Insets) selectionInsets.clone() : null;
|
||||||
|
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
|
||||||
|
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
|
||||||
|
|
||||||
|
if( column >= 0 ) {
|
||||||
|
// selection insets
|
||||||
|
|
||||||
|
// selection arc
|
||||||
|
if( column > 0 ) {
|
||||||
|
if( insets != null )
|
||||||
|
insets.left = 0;
|
||||||
|
|
||||||
|
if( table.getComponentOrientation().isLeftToRight() )
|
||||||
|
arcTopLeft = arcBottomLeft = 0;
|
||||||
|
else
|
||||||
|
arcTopRight = arcBottomRight = 0;
|
||||||
|
}
|
||||||
|
if( column < table.getColumnCount() - 1 ) {
|
||||||
|
if( insets != null )
|
||||||
|
insets.right = 0;
|
||||||
|
|
||||||
|
if( table.getComponentOrientation().isLeftToRight() )
|
||||||
|
arcTopRight = arcBottomRight = 0;
|
||||||
|
else
|
||||||
|
arcTopLeft = arcBottomLeft = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
|
||||||
|
UIScale.scale( insets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paints (rounded) cell selection.
|
||||||
|
* Supports {@link #selectionArc} and {@link #selectionInsets}.
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> This method is only invoked if either selection arc
|
||||||
|
* is greater than zero or if selection insets are not empty.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
protected void paintCellSelection( Graphics g, int row, int column, int x, int y, int width, int height ) {
|
||||||
|
boolean rowSelAllowed = table.getRowSelectionAllowed();
|
||||||
|
boolean colSelAllowed = table.getColumnSelectionAllowed();
|
||||||
|
boolean rowSelOnly = rowSelAllowed && !colSelAllowed;
|
||||||
|
boolean colSelOnly = colSelAllowed && !rowSelAllowed;
|
||||||
|
boolean cellOnlySel = rowSelAllowed && colSelAllowed;
|
||||||
|
|
||||||
|
// get selection state of surrounding cells
|
||||||
|
boolean leftSelected = (column > 0 && (rowSelOnly || table.isCellSelected( row, column - 1 )));
|
||||||
|
boolean topSelected = (row > 0 && (colSelOnly || table.isCellSelected( row - 1, column )));
|
||||||
|
boolean rightSelected = (column < table.getColumnCount() - 1 && (rowSelOnly || table.isCellSelected( row, column + 1 )));
|
||||||
|
boolean bottomSelected = (row < table.getRowCount() - 1 && (colSelOnly || table.isCellSelected( row + 1, column )));
|
||||||
|
if( !table.getComponentOrientation().isLeftToRight() ) {
|
||||||
|
boolean temp = leftSelected;
|
||||||
|
leftSelected = rightSelected;
|
||||||
|
rightSelected = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// selection insets
|
||||||
|
// (insets are applied to whole row if row-only selection is used,
|
||||||
|
// or to whole column if column-only selection is used,
|
||||||
|
// or to cell if cell selection is used)
|
||||||
|
Insets insets = (selectionInsets != null) ? (Insets) selectionInsets.clone() : null;
|
||||||
|
if( insets != null ) {
|
||||||
|
if( rowSelOnly && leftSelected )
|
||||||
|
insets.left = 0;
|
||||||
|
if( rowSelOnly && rightSelected )
|
||||||
|
insets.right = 0;
|
||||||
|
if( colSelOnly && topSelected )
|
||||||
|
insets.top = 0;
|
||||||
|
if( colSelOnly && bottomSelected )
|
||||||
|
insets.bottom = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// selection arc
|
||||||
|
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
|
||||||
|
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
|
||||||
|
if( selectionArc > 0 ) {
|
||||||
|
// note that intercellSpacing is not considered as a gap because
|
||||||
|
// grid lines are usually painted to intercell space
|
||||||
|
boolean hasRowGap = (rowSelOnly || cellOnlySel) && insets != null && (insets.top != 0 || insets.bottom != 0);
|
||||||
|
boolean hasColGap = (colSelOnly || cellOnlySel) && insets != null && (insets.left != 0 || insets.right != 0);
|
||||||
|
|
||||||
|
if( leftSelected && !hasColGap )
|
||||||
|
arcTopLeft = arcBottomLeft = 0;
|
||||||
|
if( rightSelected && !hasColGap )
|
||||||
|
arcTopRight = arcBottomRight = 0;
|
||||||
|
if( topSelected && !hasRowGap )
|
||||||
|
arcTopLeft = arcTopRight = 0;
|
||||||
|
if( bottomSelected && !hasRowGap )
|
||||||
|
arcBottomLeft = arcBottomRight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
|
||||||
|
UIScale.scale( insets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paints a cell selection at the given coordinates.
|
||||||
|
* The selection color must be set on the graphics context.
|
||||||
|
* <p>
|
||||||
|
* This method is intended for use in custom cell renderers to support
|
||||||
|
* {@link #selectionArc} and {@link #selectionInsets}.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static void paintCellSelection( JTable table, Graphics g, int row, int column, int x, int y, int width, int height ) {
|
||||||
|
if( !(table.getUI() instanceof FlatTableUI) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
FlatTableUI ui = (FlatTableUI) table.getUI();
|
||||||
|
ui.paintCellSelection( g, row, column, x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installRepaintRoundedSelectionListeners() {
|
||||||
|
if( rowSelectionListener == null ) {
|
||||||
|
rowSelectionListener = this::repaintRoundedRowSelection;
|
||||||
|
table.getSelectionModel().addListSelectionListener( rowSelectionListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( columnSelectionListener == null ) {
|
||||||
|
columnSelectionListener = new TableColumnModelListener() {
|
||||||
|
@Override
|
||||||
|
public void columnSelectionChanged( ListSelectionEvent e ) {
|
||||||
|
repaintRoundedColumnSelection( e );
|
||||||
|
}
|
||||||
|
@Override public void columnRemoved( TableColumnModelEvent e ) {}
|
||||||
|
@Override public void columnMoved( TableColumnModelEvent e ) {}
|
||||||
|
@Override public void columnMarginChanged( ChangeEvent e ) {}
|
||||||
|
@Override public void columnAdded( TableColumnModelEvent e ) {}
|
||||||
|
};
|
||||||
|
table.getColumnModel().addColumnModelListener( columnSelectionListener );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintRoundedRowSelection( ListSelectionEvent e ) {
|
||||||
|
if( selectionArc <= 0 || !table.getRowSelectionAllowed() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
int rowCount = table.getRowCount();
|
||||||
|
int columnCount = table.getColumnCount();
|
||||||
|
if( rowCount <= 0 || columnCount <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// repaint including rows before and after changed selection
|
||||||
|
int firstRow = Math.max( 0, Math.min( e.getFirstIndex() - 1, rowCount - 1 ) );
|
||||||
|
int lastRow = Math.max( 0, Math.min( e.getLastIndex() + 1, rowCount - 1 ) );
|
||||||
|
Rectangle firstRect = table.getCellRect( firstRow, 0, false );
|
||||||
|
Rectangle lastRect = table.getCellRect( lastRow, columnCount - 1, false );
|
||||||
|
table.repaint( firstRect.union( lastRect ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintRoundedColumnSelection( ListSelectionEvent e ) {
|
||||||
|
if( selectionArc <= 0 || !table.getColumnSelectionAllowed() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
int rowCount = table.getRowCount();
|
||||||
|
int columnCount = table.getColumnCount();
|
||||||
|
if( rowCount <= 0 || columnCount <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// limit to selected rows for cell selection
|
||||||
|
int firstRow = 0;
|
||||||
|
int lastRow = rowCount - 1;
|
||||||
|
if( table.getRowSelectionAllowed() ) {
|
||||||
|
firstRow = table.getSelectionModel().getMinSelectionIndex();
|
||||||
|
lastRow = table.getSelectionModel().getMaxSelectionIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
// repaint including columns before and after changed selection
|
||||||
|
int firstColumn = Math.max( 0, Math.min( e.getFirstIndex() - 1, columnCount - 1 ) );
|
||||||
|
int lastColumn = Math.max( 0, Math.min( e.getLastIndex() + 1, columnCount - 1 ) );
|
||||||
|
Rectangle firstRect = table.getCellRect( firstRow, firstColumn, false );
|
||||||
|
Rectangle lastRect = table.getCellRect( lastRow, lastColumn, false );
|
||||||
|
table.repaint( firstRect.union( lastRect ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class RoundedSelectionGraphics -------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because selection painting is done in the cell renderer, it would be
|
||||||
|
* necessary to require a FlatLaf specific renderer to implement rounded selection.
|
||||||
|
* Using a LaF specific renderer was avoided because often a custom renderer is
|
||||||
|
* already used in applications. Then either the rounded selection is not used,
|
||||||
|
* or the application has to be changed to extend a FlatLaf renderer.
|
||||||
|
* <p>
|
||||||
|
* To solve this, a graphics proxy is used that paints rounded selection
|
||||||
|
* if row/column/cell is selected and the renderer wants to fill the background.
|
||||||
|
*/
|
||||||
|
private class RoundedSelectionGraphics
|
||||||
|
extends Graphics2DProxy
|
||||||
|
{
|
||||||
|
private final Color alternateRowColor;
|
||||||
|
|
||||||
|
// used to avoid endless loop in case that paintCellSelection() invokes
|
||||||
|
// g.fillRect() with full bounds (selectionInsets is 0,0,0,0)
|
||||||
|
private boolean inPaintSelection;
|
||||||
|
|
||||||
|
RoundedSelectionGraphics( Graphics delegate, Color alternateRowColor ) {
|
||||||
|
super( (Graphics2D) delegate );
|
||||||
|
this.alternateRowColor = alternateRowColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Graphics create() {
|
||||||
|
return new RoundedSelectionGraphics( super.create(), alternateRowColor );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Graphics create( int x, int y, int width, int height ) {
|
||||||
|
return new RoundedSelectionGraphics( super.create( x, y, width, height ), alternateRowColor );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillRect( int x, int y, int width, int height ) {
|
||||||
|
if( fillCellSelection( x, y, width, height ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
super.fillRect( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fill( Shape shape ) {
|
||||||
|
if( shape instanceof Rectangle2D ) {
|
||||||
|
Rectangle2D r = (Rectangle2D) shape;
|
||||||
|
double x = r.getX();
|
||||||
|
double y = r.getY();
|
||||||
|
double width = r.getWidth();
|
||||||
|
double height = r.getHeight();
|
||||||
|
if( x == (int) x && y == (int) y && width == (int) width && height == (int) height ) {
|
||||||
|
if( fillCellSelection( (int) x, (int) y, (int) width, (int) height ) )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.fill( shape );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean fillCellSelection( int x, int y, int width, int height ) {
|
||||||
|
if( inPaintSelection )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Color color;
|
||||||
|
Component rendererComponent;
|
||||||
|
if( x == 0 && y == 0 &&
|
||||||
|
((color = getColor()) == table.getSelectionBackground() ||
|
||||||
|
(alternateRowColor != null && color == alternateRowColor)) &&
|
||||||
|
(rendererComponent = findActiveRendererComponent()) != null &&
|
||||||
|
width == rendererComponent.getWidth() &&
|
||||||
|
height == rendererComponent.getHeight() )
|
||||||
|
{
|
||||||
|
Point location = rendererComponent.getLocation();
|
||||||
|
int row = table.rowAtPoint( location );
|
||||||
|
int column = table.columnAtPoint( location );
|
||||||
|
if( row >= 0 && column >= 0 ) {
|
||||||
|
inPaintSelection = true;
|
||||||
|
if( color == table.getSelectionBackground() )
|
||||||
|
paintCellSelection( this, row, column, x, y, width, height );
|
||||||
|
else
|
||||||
|
paintAlternateRowBackground( this, row, column, x, y, width, height );
|
||||||
|
inPaintSelection = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CellRendererPane may contain multiple components, if multiple renderers
|
||||||
|
* are used. Inactive renderer components have size {@code 0x0}.
|
||||||
|
*/
|
||||||
|
private Component findActiveRendererComponent() {
|
||||||
|
int count = rendererPane.getComponentCount();
|
||||||
|
for( int i = 0; i < count; i++ ) {
|
||||||
|
Component c = rendererPane.getComponent( i );
|
||||||
|
if( c.getWidth() > 0 && c.getHeight() > 0 )
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class OutsideAlternateRowsListener ---------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used if table auto-resize-mode is off to repaint outside alternate rows
|
||||||
|
* when table width changed (column resized) or component orientation changed.
|
||||||
|
*/
|
||||||
|
private class FlatOutsideAlternateRowsListener
|
||||||
|
extends ComponentAdapter
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void componentHidden( ComponentEvent e ) {
|
||||||
|
Container viewport = SwingUtilities.getUnwrappedParent( table );
|
||||||
|
if( viewport instanceof JViewport )
|
||||||
|
HiDPIUtils.repaint( viewport );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentMoved( ComponentEvent e ) {
|
||||||
|
repaintAreaBelowTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentResized( ComponentEvent e ) {
|
||||||
|
repaintAreaBelowTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintAreaBelowTable() {
|
||||||
|
Container viewport = SwingUtilities.getUnwrappedParent( table );
|
||||||
|
if( viewport instanceof JViewport ) {
|
||||||
|
int viewportHeight = viewport.getHeight();
|
||||||
|
int tableHeight = table.getHeight();
|
||||||
|
if( tableHeight < viewportHeight )
|
||||||
|
HiDPIUtils.repaint( viewport, 0, tableHeight, viewport.getWidth(), viewportHeight - tableHeight );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatTablePropertyWatcher -------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener that watches for change of some table properties from application code.
|
||||||
|
* This information is used in {@link FlatTableUI#installDefaults()} and
|
||||||
|
* {@link FlatTableUI#uninstallDefaults()} to decide whether FlatLaf modifies those properties.
|
||||||
|
* If they are modified in application code, FlatLaf no longer changes them.
|
||||||
|
*
|
||||||
|
* The listener is added once for each table, but never removed.
|
||||||
|
* So switching Laf/theme reuses existing listener.
|
||||||
|
*/
|
||||||
|
private static class FlatTablePropertyWatcher
|
||||||
|
implements PropertyChangeListener
|
||||||
|
{
|
||||||
|
boolean enabled = true;
|
||||||
|
boolean showHorizontalLinesChanged;
|
||||||
|
boolean showVerticalLinesChanged;
|
||||||
|
boolean intercellSpacingChanged;
|
||||||
|
|
||||||
|
static FlatTablePropertyWatcher get( JTable table ) {
|
||||||
|
for( PropertyChangeListener l : table.getPropertyChangeListeners() ) {
|
||||||
|
if( l instanceof FlatTablePropertyWatcher )
|
||||||
|
return (FlatTablePropertyWatcher) l;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface PropertyChangeListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
if( !enabled )
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case "showHorizontalLines": showHorizontalLinesChanged = true; break;
|
||||||
|
case "showVerticalLines": showVerticalLinesChanged = true; break;
|
||||||
|
case "rowMargin": intercellSpacingChanged = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatBooleanRenderer ------------------------------------------
|
||||||
|
|
||||||
|
private static class FlatBooleanRenderer
|
||||||
|
extends DefaultTableCellRenderer
|
||||||
|
implements UIResource
|
||||||
|
{
|
||||||
|
private boolean selected;
|
||||||
|
|
||||||
|
FlatBooleanRenderer() {
|
||||||
|
setHorizontalAlignment( SwingConstants.CENTER );
|
||||||
|
setIcon( new FlatCheckBoxIcon() {
|
||||||
|
@Override
|
||||||
|
protected boolean isSelected( Component c ) {
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setValue( Object value ) {
|
||||||
|
selected = (value != null && (Boolean) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class StartEditingAction -------------------------------------------
|
||||||
|
|
||||||
|
private static class StartEditingAction
|
||||||
|
extends FlatUIAction
|
||||||
|
{
|
||||||
|
static void install( ActionMap map, String key ) {
|
||||||
|
Action oldAction = map.get( key );
|
||||||
|
if( oldAction == null || oldAction instanceof StartEditingAction )
|
||||||
|
return; // not found or already installed
|
||||||
|
|
||||||
|
map.put( key, new StartEditingAction( oldAction ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private StartEditingAction( Action delegate ) {
|
||||||
|
super( delegate );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed( ActionEvent e ) {
|
||||||
|
JTable table = (JTable) e.getSource();
|
||||||
|
|
||||||
|
Component oldEditorComp = table.getEditorComponent();
|
||||||
|
|
||||||
|
delegate.actionPerformed( e );
|
||||||
|
|
||||||
|
// select all text in editor if editing starts with F2 key
|
||||||
|
Component editorComp = table.getEditorComponent();
|
||||||
|
if( oldEditorComp == null && editorComp instanceof JTextField )
|
||||||
|
((JTextField)editorComp).selectAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ public class FlatTextFieldUI
|
|||||||
case COMPONENT_ROUND_RECT:
|
case COMPONENT_ROUND_RECT:
|
||||||
case OUTLINE:
|
case OUTLINE:
|
||||||
case TEXT_FIELD_PADDING:
|
case TEXT_FIELD_PADDING:
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MINIMUM_WIDTH:
|
case MINIMUM_WIDTH:
|
||||||
@@ -250,38 +250,38 @@ public class FlatTextFieldUI
|
|||||||
case STYLE_CLASS:
|
case STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_FIELD_LEADING_ICON:
|
case TEXT_FIELD_LEADING_ICON:
|
||||||
leadingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
|
leadingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_FIELD_TRAILING_ICON:
|
case TEXT_FIELD_TRAILING_ICON:
|
||||||
trailingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
|
trailingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_FIELD_LEADING_COMPONENT:
|
case TEXT_FIELD_LEADING_COMPONENT:
|
||||||
uninstallLeadingComponent();
|
uninstallLeadingComponent();
|
||||||
installLeadingComponent();
|
installLeadingComponent();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_FIELD_TRAILING_COMPONENT:
|
case TEXT_FIELD_TRAILING_COMPONENT:
|
||||||
uninstallTrailingComponent();
|
uninstallTrailingComponent();
|
||||||
installTrailingComponent();
|
installTrailingComponent();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_FIELD_SHOW_CLEAR_BUTTON:
|
case TEXT_FIELD_SHOW_CLEAR_BUTTON:
|
||||||
uninstallClearButton();
|
uninstallClearButton();
|
||||||
installClearButton();
|
installClearButton();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "enabled":
|
case "enabled":
|
||||||
@@ -815,7 +815,7 @@ debug*/
|
|||||||
if( visible != clearButton.isVisible() ) {
|
if( visible != clearButton.isVisible() ) {
|
||||||
clearButton.setVisible( visible );
|
clearButton.setVisible( visible );
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import java.awt.Rectangle;
|
|||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.ComponentAdapter;
|
||||||
import java.awt.event.ComponentEvent;
|
import java.awt.event.ComponentEvent;
|
||||||
import java.awt.event.ComponentListener;
|
import java.awt.event.ComponentListener;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
@@ -46,9 +47,9 @@ import java.awt.event.WindowEvent;
|
|||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Function;
|
||||||
import javax.accessibility.AccessibleContext;
|
import javax.accessibility.AccessibleContext;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
@@ -57,7 +58,6 @@ import javax.swing.Icon;
|
|||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JInternalFrame;
|
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@@ -66,6 +66,7 @@ import javax.swing.SwingUtilities;
|
|||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.AbstractBorder;
|
import javax.swing.border.AbstractBorder;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatNativeWindowBorder.WindowTopBorder;
|
import com.formdev.flatlaf.ui.FlatNativeWindowBorder.WindowTopBorder;
|
||||||
@@ -98,7 +99,6 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TitlePane.showIconBesideTitle boolean
|
* @uiDefault TitlePane.showIconBesideTitle boolean
|
||||||
* @uiDefault TitlePane.menuBarTitleGap int
|
* @uiDefault TitlePane.menuBarTitleGap int
|
||||||
* @uiDefault TitlePane.menuBarTitleMinimumGap int
|
* @uiDefault TitlePane.menuBarTitleMinimumGap int
|
||||||
* @uiDefault TitlePane.menuBarResizeHeight int
|
|
||||||
* @uiDefault TitlePane.closeIcon Icon
|
* @uiDefault TitlePane.closeIcon Icon
|
||||||
* @uiDefault TitlePane.iconifyIcon Icon
|
* @uiDefault TitlePane.iconifyIcon Icon
|
||||||
* @uiDefault TitlePane.maximizeIcon Icon
|
* @uiDefault TitlePane.maximizeIcon Icon
|
||||||
@@ -109,7 +109,8 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatTitlePane
|
public class FlatTitlePane
|
||||||
extends JComponent
|
extends JComponent
|
||||||
{
|
{
|
||||||
private static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles";
|
static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles";
|
||||||
|
private static final boolean isWindows_10 = SystemInfo.isWindows_10_orLater && !SystemInfo.isWindows_11_orLater;
|
||||||
|
|
||||||
/** @since 2.5 */ protected final Font titleFont;
|
/** @since 2.5 */ protected final Font titleFont;
|
||||||
protected final Color activeBackground;
|
protected final Color activeBackground;
|
||||||
@@ -131,7 +132,6 @@ public class FlatTitlePane
|
|||||||
/** @since 2.4 */ protected final boolean showIconBesideTitle;
|
/** @since 2.4 */ protected final boolean showIconBesideTitle;
|
||||||
protected final int menuBarTitleGap;
|
protected final int menuBarTitleGap;
|
||||||
/** @since 2.4 */ protected final int menuBarTitleMinimumGap;
|
/** @since 2.4 */ protected final int menuBarTitleMinimumGap;
|
||||||
/** @since 2.4 */ protected final int menuBarResizeHeight;
|
|
||||||
|
|
||||||
protected final JRootPane rootPane;
|
protected final JRootPane rootPane;
|
||||||
protected final String windowStyle;
|
protected final String windowStyle;
|
||||||
@@ -150,6 +150,33 @@ public class FlatTitlePane
|
|||||||
|
|
||||||
private final Handler handler;
|
private final Handler handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This panel handles mouse events if FlatLaf window decorations are used
|
||||||
|
* without native window border. E.g. on Linux.
|
||||||
|
* <p>
|
||||||
|
* This panel usually has same bounds as the title pane,
|
||||||
|
* except if fullWindowContent mode is enabled.
|
||||||
|
* <p>
|
||||||
|
* This panel is not a child of the title pane.
|
||||||
|
* Instead it is added by FlatRootPaneUI to the layered pane at a layer
|
||||||
|
* under the title pane and under the frame content.
|
||||||
|
* The separation is necessary for fullWindowContent mode, where the title pane
|
||||||
|
* is layered over the frame content (for title pane buttons), but the mousePanel
|
||||||
|
* needs to be layered under the frame content so that components on content pane
|
||||||
|
* can receive mouse events when located in title area.
|
||||||
|
*/
|
||||||
|
final JPanel mouseLayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This panel paint a border at the top of the window in fullWindowContent mode,
|
||||||
|
* if FlatLaf window decorations are enabled.
|
||||||
|
* Only used on Windows 10.
|
||||||
|
* <p>
|
||||||
|
* This panel is not a child of the title pane.
|
||||||
|
* Instead it is added by FlatRootPaneUI to the layered pane at a layer over all other layers.
|
||||||
|
*/
|
||||||
|
final JPanel windowTopBorderLayer;
|
||||||
|
|
||||||
public FlatTitlePane( JRootPane rootPane ) {
|
public FlatTitlePane( JRootPane rootPane ) {
|
||||||
this.rootPane = rootPane;
|
this.rootPane = rootPane;
|
||||||
|
|
||||||
@@ -178,7 +205,6 @@ public class FlatTitlePane
|
|||||||
showIconBesideTitle = FlatUIUtils.getSubUIBoolean( "TitlePane.showIconBesideTitle", windowStyle, false );
|
showIconBesideTitle = FlatUIUtils.getSubUIBoolean( "TitlePane.showIconBesideTitle", windowStyle, false );
|
||||||
menuBarTitleGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleGap", windowStyle, 40 );
|
menuBarTitleGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleGap", windowStyle, 40 );
|
||||||
menuBarTitleMinimumGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleMinimumGap", windowStyle, 12 );
|
menuBarTitleMinimumGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleMinimumGap", windowStyle, 12 );
|
||||||
menuBarResizeHeight = FlatUIUtils.getSubUIInt( "TitlePane.menuBarResizeHeight", windowStyle, 4 );
|
|
||||||
|
|
||||||
|
|
||||||
handler = createHandler();
|
handler = createHandler();
|
||||||
@@ -187,11 +213,18 @@ public class FlatTitlePane
|
|||||||
addSubComponents();
|
addSubComponents();
|
||||||
activeChanged( true );
|
activeChanged( true );
|
||||||
|
|
||||||
addMouseListener( handler );
|
mouseLayer = new JPanel();
|
||||||
addMouseMotionListener( handler );
|
mouseLayer.setOpaque( false );
|
||||||
|
mouseLayer.addMouseListener( handler );
|
||||||
|
mouseLayer.addMouseMotionListener( handler );
|
||||||
|
|
||||||
// necessary for closing window with double-click on icon
|
if( isWindows_10 && FlatNativeWindowBorder.isSupported() ) {
|
||||||
iconLabel.addMouseListener( handler );
|
windowTopBorderLayer = new JPanel();
|
||||||
|
windowTopBorderLayer.setVisible( false );
|
||||||
|
windowTopBorderLayer.setOpaque( false );
|
||||||
|
windowTopBorderLayer.setBorder( FlatUIUtils.nonUIResource( WindowTopBorder.getInstance() ) );
|
||||||
|
} else
|
||||||
|
windowTopBorderLayer = null;
|
||||||
|
|
||||||
applyComponentOrientation( rootPane.getComponentOrientation() );
|
applyComponentOrientation( rootPane.getComponentOrientation() );
|
||||||
}
|
}
|
||||||
@@ -234,6 +267,11 @@ public class FlatTitlePane
|
|||||||
setLayout( new BorderLayout() {
|
setLayout( new BorderLayout() {
|
||||||
@Override
|
@Override
|
||||||
public void layoutContainer( Container target ) {
|
public void layoutContainer( Container target ) {
|
||||||
|
if( isFullWindowContent() ) {
|
||||||
|
super.layoutContainer( target );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// compute available bounds
|
// compute available bounds
|
||||||
Insets insets = target.getInsets();
|
Insets insets = target.getInsets();
|
||||||
int x = insets.left;
|
int x = insets.left;
|
||||||
@@ -247,7 +285,7 @@ public class FlatTitlePane
|
|||||||
int titleWidth = w - leftWidth - buttonsWidth;
|
int titleWidth = w - leftWidth - buttonsWidth;
|
||||||
int minTitleWidth = UIScale.scale( titleMinimumWidth );
|
int minTitleWidth = UIScale.scale( titleMinimumWidth );
|
||||||
|
|
||||||
// increase minimum width if icon is show besides the title
|
// increase minimum width if icon is shown besides the title
|
||||||
Icon icon = titleLabel.getIcon();
|
Icon icon = titleLabel.getIcon();
|
||||||
if( icon != null ) {
|
if( icon != null ) {
|
||||||
Insets iconInsets = iconLabel.getInsets();
|
Insets iconInsets = iconLabel.getInsets();
|
||||||
@@ -295,6 +333,9 @@ public class FlatTitlePane
|
|||||||
horizontalGlue.getWidth(), titleLabel.getHeight() );
|
horizontalGlue.getWidth(), titleLabel.getHeight() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear hit-test cache
|
||||||
|
lastCaptionHitTestTime = 0;
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -318,6 +359,10 @@ public class FlatTitlePane
|
|||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
Dimension size = super.getPreferredSize();
|
Dimension size = super.getPreferredSize();
|
||||||
|
int titleBarHeight = clientPropertyInt( rootPane, TITLE_BAR_HEIGHT, -1 );
|
||||||
|
if( titleBarHeight >= 0 )
|
||||||
|
return new Dimension( size.width, UIScale.scale( titleBarHeight ) );
|
||||||
|
|
||||||
if( buttonMaximizedHeight > 0 && isWindowMaximized() && !hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ) ) {
|
if( buttonMaximizedHeight > 0 && isWindowMaximized() && !hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ) ) {
|
||||||
// make title pane height smaller when frame is maximized
|
// make title pane height smaller when frame is maximized
|
||||||
size = new Dimension( size.width, Math.min( size.height, UIScale.scale( buttonMaximizedHeight ) ) );
|
size = new Dimension( size.width, Math.min( size.height, UIScale.scale( buttonMaximizedHeight ) ) );
|
||||||
@@ -338,6 +383,13 @@ public class FlatTitlePane
|
|||||||
buttonPanel.add( restoreButton );
|
buttonPanel.add( restoreButton );
|
||||||
}
|
}
|
||||||
buttonPanel.add( closeButton );
|
buttonPanel.add( closeButton );
|
||||||
|
|
||||||
|
ComponentListener l = new ComponentAdapter() {
|
||||||
|
@Override public void componentResized( ComponentEvent e ) { updateFullWindowContentButtonsBoundsProperty(); }
|
||||||
|
@Override public void componentMoved( ComponentEvent e ) { updateFullWindowContentButtonsBoundsProperty(); }
|
||||||
|
};
|
||||||
|
buttonPanel.addComponentListener( l );
|
||||||
|
addComponentListener( l );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) {
|
protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) {
|
||||||
@@ -347,6 +399,12 @@ public class FlatTitlePane
|
|||||||
// allow the button to shrink if space is rare
|
// allow the button to shrink if space is rare
|
||||||
return new Dimension( UIScale.scale( buttonMinimumWidth ), super.getMinimumSize().height );
|
return new Dimension( UIScale.scale( buttonMinimumWidth ), super.getMinimumSize().height );
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public Dimension getMaximumSize() {
|
||||||
|
// allow the button to fill whole button area height
|
||||||
|
// see also BasicMenuUI.getMaximumSize()
|
||||||
|
return new Dimension( super.getMaximumSize().width, Short.MAX_VALUE );
|
||||||
|
}
|
||||||
};
|
};
|
||||||
button.setFocusable( false );
|
button.setFocusable( false );
|
||||||
button.setContentAreaFilled( false );
|
button.setContentAreaFilled( false );
|
||||||
@@ -417,7 +475,9 @@ public class FlatTitlePane
|
|||||||
|
|
||||||
/** @since 3 */
|
/** @since 3 */
|
||||||
protected void updateVisibility() {
|
protected void updateVisibility() {
|
||||||
titleLabel.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_TITLE, true ) );
|
boolean isFullWindowContent = isFullWindowContent();
|
||||||
|
leftPanel.setVisible( !isFullWindowContent );
|
||||||
|
titleLabel.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_TITLE, true ) && !isFullWindowContent );
|
||||||
closeButton.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_CLOSE, true ) );
|
closeButton.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_CLOSE, true ) );
|
||||||
|
|
||||||
if( window instanceof Frame ) {
|
if( window instanceof Frame ) {
|
||||||
@@ -443,7 +503,7 @@ public class FlatTitlePane
|
|||||||
|
|
||||||
// get window images
|
// get window images
|
||||||
List<Image> images = null;
|
List<Image> images = null;
|
||||||
if( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_ICON, defaultShowIcon ) ) {
|
if( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_ICON, defaultShowIcon ) && !isFullWindowContent() ) {
|
||||||
images = window.getIconImages();
|
images = window.getIconImages();
|
||||||
if( images.isEmpty() ) {
|
if( images.isEmpty() ) {
|
||||||
// search in owners
|
// search in owners
|
||||||
@@ -468,6 +528,13 @@ public class FlatTitlePane
|
|||||||
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateFullWindowContentButtonsBoundsProperty() {
|
||||||
|
Rectangle bounds = isFullWindowContent()
|
||||||
|
? new Rectangle( SwingUtilities.convertPoint( buttonPanel, 0, 0, rootPane ), buttonPanel.getSize() )
|
||||||
|
: null;
|
||||||
|
rootPane.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS, bounds );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addNotify() {
|
public void addNotify() {
|
||||||
super.addNotify();
|
super.addNotify();
|
||||||
@@ -522,6 +589,11 @@ public class FlatTitlePane
|
|||||||
window.removeComponentListener( handler );
|
window.removeComponentListener( handler );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.4 */
|
||||||
|
protected boolean isFullWindowContent() {
|
||||||
|
return FlatRootPaneUI.isFullWindowContent( rootPane );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this title pane currently has a visible and embedded menubar.
|
* Returns whether this title pane currently has a visible and embedded menubar.
|
||||||
*/
|
*/
|
||||||
@@ -533,6 +605,9 @@ public class FlatTitlePane
|
|||||||
* Returns whether the menubar should be embedded into the title pane.
|
* Returns whether the menubar should be embedded into the title pane.
|
||||||
*/
|
*/
|
||||||
protected boolean isMenuBarEmbedded() {
|
protected boolean isMenuBarEmbedded() {
|
||||||
|
if( isFullWindowContent() )
|
||||||
|
return false;
|
||||||
|
|
||||||
// not storing value of "TitlePane.menuBarEmbedded" in class to allow changing at runtime
|
// not storing value of "TitlePane.menuBarEmbedded" in class to allow changing at runtime
|
||||||
return FlatUIUtils.getBoolean( rootPane,
|
return FlatUIUtils.getBoolean( rootPane,
|
||||||
FlatSystemProperties.MENUBAR_EMBEDDED,
|
FlatSystemProperties.MENUBAR_EMBEDDED,
|
||||||
@@ -608,6 +683,10 @@ public class FlatTitlePane
|
|||||||
doLayout();
|
doLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void menuBarInvalidate() {
|
||||||
|
menuBarPlaceholder.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g ) {
|
public void paint( Graphics g ) {
|
||||||
super.paint( g );
|
super.paint( g );
|
||||||
@@ -616,21 +695,45 @@ public class FlatTitlePane
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if( debugTitleBarHeight > 0 ) {
|
if( debugTitleBarHeight > 0 ) {
|
||||||
|
// title bar height is measured from window top edge
|
||||||
|
int y = SwingUtilities.convertPoint( window, 0, debugTitleBarHeight, this ).y;
|
||||||
g.setColor( Color.green );
|
g.setColor( Color.green );
|
||||||
g.drawLine( 0, debugTitleBarHeight, getWidth(), debugTitleBarHeight );
|
g.drawLine( 0, y, getWidth(), y );
|
||||||
}
|
|
||||||
if( debugHitTestSpots != null ) {
|
|
||||||
for( Rectangle r : debugHitTestSpots )
|
|
||||||
paintRect( g, Color.red, r );
|
|
||||||
}
|
|
||||||
paintRect( g, Color.cyan, debugCloseButtonBounds );
|
|
||||||
paintRect( g, Color.blue, debugAppIconBounds );
|
|
||||||
paintRect( g, Color.blue, debugMinimizeButtonBounds );
|
|
||||||
paintRect( g, Color.magenta, debugMaximizeButtonBounds );
|
|
||||||
paintRect( g, Color.cyan, debugCloseButtonBounds );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void paintRect( Graphics g, Color color, Rectangle r ) {
|
g.setColor( Color.red );
|
||||||
|
debugPaintComponentWithMouseListener( g, Color.red, rootPane.getLayeredPane(), 0, 0 );
|
||||||
|
|
||||||
|
debugPaintRect( g, Color.blue, debugAppIconBounds );
|
||||||
|
debugPaintRect( g, Color.blue, debugMinimizeButtonBounds );
|
||||||
|
debugPaintRect( g, Color.magenta, debugMaximizeButtonBounds );
|
||||||
|
debugPaintRect( g, Color.cyan, debugCloseButtonBounds );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void debugPaintComponentWithMouseListener( Graphics g, Color color, Component c, int x, int y ) {
|
||||||
|
if( !c.isDisplayable() || !c.isVisible() || c == mouseLayer ||
|
||||||
|
c == iconifyButton || c == maximizeButton || c == restoreButton || c == closeButton )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( c.getMouseListeners().length > 0 ||
|
||||||
|
c.getMouseMotionListeners().length > 0 ||
|
||||||
|
c.getMouseWheelListeners().length > 0 )
|
||||||
|
{
|
||||||
|
g.drawRect( x, y, c.getWidth(), c.getHeight() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( c instanceof Container ) {
|
||||||
|
Rectangle titlePaneBoundsOnWindow = SwingUtilities.convertRectangle( this, new Rectangle( getSize() ), window );
|
||||||
|
for( Component child : ((Container)c).getComponents() ) {
|
||||||
|
Rectangle compBoundsOnWindow = SwingUtilities.convertRectangle( c, new Rectangle( c.getSize() ), window );
|
||||||
|
if( compBoundsOnWindow.intersects( titlePaneBoundsOnWindow ) )
|
||||||
|
debugPaintComponentWithMouseListener( g, color, child, x + child.getX(), y + child.getY() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void debugPaintRect( Graphics g, Color color, Rectangle r ) {
|
||||||
if( r == null )
|
if( r == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -641,6 +744,9 @@ public class FlatTitlePane
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintComponent( Graphics g ) {
|
protected void paintComponent( Graphics g ) {
|
||||||
|
if( isFullWindowContent() )
|
||||||
|
return;
|
||||||
|
|
||||||
// not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime
|
// not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime
|
||||||
g.setColor( (UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
|
g.setColor( (UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
|
||||||
clientPropertyColor( rootPane, TITLE_BAR_BACKGROUND, null ) == null)
|
clientPropertyColor( rootPane, TITLE_BAR_BACKGROUND, null ) == null)
|
||||||
@@ -649,16 +755,6 @@ public class FlatTitlePane
|
|||||||
g.fillRect( 0, 0, getWidth(), getHeight() );
|
g.fillRect( 0, 0, getWidth(), getHeight() );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void repaintWindowBorder() {
|
|
||||||
int width = rootPane.getWidth();
|
|
||||||
int height = rootPane.getHeight();
|
|
||||||
Insets insets = rootPane.getInsets();
|
|
||||||
rootPane.repaint( 0, 0, width, insets.top ); // top
|
|
||||||
rootPane.repaint( 0, 0, insets.left, height ); // left
|
|
||||||
rootPane.repaint( 0, height - insets.bottom, width, insets.bottom ); // bottom
|
|
||||||
rootPane.repaint( width - insets.right, 0, insets.right, height ); // right
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iconifies the window.
|
* Iconifies the window.
|
||||||
*/
|
*/
|
||||||
@@ -835,10 +931,6 @@ public class FlatTitlePane
|
|||||||
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) );
|
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasJBRCustomDecoration() {
|
|
||||||
return window != null && JBRCustomDecorations.hasCustomDecoration( window );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether windows uses native window border and has custom decorations enabled.
|
* Returns whether windows uses native window border and has custom decorations enabled.
|
||||||
*/
|
*/
|
||||||
@@ -846,6 +938,10 @@ public class FlatTitlePane
|
|||||||
return window != null && FlatNativeWindowBorder.hasCustomDecoration( window );
|
return window != null && FlatNativeWindowBorder.hasCustomDecoration( window );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isWindowTopBorderNeeded() {
|
||||||
|
return isWindows_10 && hasNativeCustomDecoration();
|
||||||
|
}
|
||||||
|
|
||||||
// used to invoke updateNativeTitleBarHeightAndHitTestSpots() only once from latest invokeLater()
|
// used to invoke updateNativeTitleBarHeightAndHitTestSpots() only once from latest invokeLater()
|
||||||
private int laterCounter;
|
private int laterCounter;
|
||||||
|
|
||||||
@@ -866,11 +962,14 @@ public class FlatTitlePane
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
int titleBarHeight = getHeight();
|
int titleBarHeight = getHeight();
|
||||||
|
// title bar height must be measured from window top edge
|
||||||
|
// (when window is maximized, window y location is e.g. -11 and window top inset is 11)
|
||||||
|
for( Component c = this; c != window && c != null; c = c.getParent() )
|
||||||
|
titleBarHeight += c.getY();
|
||||||
// slightly reduce height so that component receives mouseExit events
|
// slightly reduce height so that component receives mouseExit events
|
||||||
if( titleBarHeight > 0 )
|
if( titleBarHeight > 0 )
|
||||||
titleBarHeight--;
|
titleBarHeight--;
|
||||||
|
|
||||||
List<Rectangle> hitTestSpots = new ArrayList<>();
|
|
||||||
Rectangle appIconBounds = null;
|
Rectangle appIconBounds = null;
|
||||||
|
|
||||||
if( !showIconBesideTitle && iconLabel.isVisible() ) {
|
if( !showIconBesideTitle && iconLabel.isVisible() ) {
|
||||||
@@ -896,9 +995,6 @@ public class FlatTitlePane
|
|||||||
iconBounds.width += iconInsets.right;
|
iconBounds.width += iconInsets.right;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( hasJBRCustomDecoration() )
|
|
||||||
hitTestSpots.add( iconBounds );
|
|
||||||
else
|
|
||||||
appIconBounds = iconBounds;
|
appIconBounds = iconBounds;
|
||||||
} else if( showIconBesideTitle && titleLabel.getIcon() != null && titleLabel.getUI() instanceof FlatTitleLabelUI ) {
|
} else if( showIconBesideTitle && titleLabel.getIcon() != null && titleLabel.getUI() instanceof FlatTitleLabelUI ) {
|
||||||
FlatTitleLabelUI ui = (FlatTitleLabelUI) titleLabel.getUI();
|
FlatTitleLabelUI ui = (FlatTitleLabelUI) titleLabel.getUI();
|
||||||
@@ -927,78 +1023,21 @@ public class FlatTitlePane
|
|||||||
iconR.width += 2;
|
iconR.width += 2;
|
||||||
iconR.height += 2;
|
iconR.height += 2;
|
||||||
|
|
||||||
if( hasJBRCustomDecoration() )
|
|
||||||
hitTestSpots.add( iconR );
|
|
||||||
else
|
|
||||||
appIconBounds = iconR;
|
appIconBounds = iconR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle r = getNativeHitTestSpot( buttonPanel );
|
|
||||||
if( r != null )
|
|
||||||
hitTestSpots.add( r );
|
|
||||||
|
|
||||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
|
||||||
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
|
|
||||||
r = getNativeHitTestSpot( menuBar );
|
|
||||||
if( r != null ) {
|
|
||||||
// if frame is resizable and not maximized, make menu bar hit test spot smaller at top
|
|
||||||
// to have a small area above the menu bar to resize the window
|
|
||||||
if( window instanceof Frame && ((Frame)window).isResizable() && !isWindowMaximized() ) {
|
|
||||||
// limit to 8, because Windows does not use a larger height
|
|
||||||
int resizeHeight = UIScale.scale( Math.min( menuBarResizeHeight, 8 ) );
|
|
||||||
r.y += resizeHeight;
|
|
||||||
r.height -= resizeHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = menuBar.getComponentCount();
|
|
||||||
for( int i = count - 1; i >= 0; i-- ) {
|
|
||||||
Component c = menuBar.getComponent( i );
|
|
||||||
if( c instanceof Box.Filler ||
|
|
||||||
(c instanceof JComponent && clientPropertyBoolean( (JComponent) c, COMPONENT_TITLE_BAR_CAPTION, false ) ) )
|
|
||||||
{
|
|
||||||
// If menu bar is embedded and contains a horizontal glue or caption component,
|
|
||||||
// then split the hit test spot so that
|
|
||||||
// the glue/caption component area can be used to move the window.
|
|
||||||
|
|
||||||
Point glueLocation = SwingUtilities.convertPoint( c, 0, 0, window );
|
|
||||||
int x2 = glueLocation.x + c.getWidth();
|
|
||||||
Rectangle r2;
|
|
||||||
if( getComponentOrientation().isLeftToRight() ) {
|
|
||||||
r2 = new Rectangle( x2, r.y, (r.x + r.width) - x2, r.height );
|
|
||||||
|
|
||||||
r.width = glueLocation.x - r.x;
|
|
||||||
} else {
|
|
||||||
r2 = new Rectangle( r.x, r.y, glueLocation.x - r.x, r.height );
|
|
||||||
|
|
||||||
r.width = (r.x + r.width) - x2;
|
|
||||||
r.x = x2;
|
|
||||||
}
|
|
||||||
if( r2.width > 0 )
|
|
||||||
hitTestSpots.add( r2 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hitTestSpots.add( r );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow internal frames in layered pane to be moved/resized when placed over title bar
|
|
||||||
for( Component c : rootPane.getLayeredPane().getComponents() ) {
|
|
||||||
r = (c instanceof JInternalFrame) ? getNativeHitTestSpot( (JInternalFrame) c ) : null;
|
|
||||||
if( r != null )
|
|
||||||
hitTestSpots.add( r );
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle minimizeButtonBounds = boundsInWindow( iconifyButton );
|
Rectangle minimizeButtonBounds = boundsInWindow( iconifyButton );
|
||||||
Rectangle maximizeButtonBounds = boundsInWindow( maximizeButton.isVisible() ? maximizeButton : restoreButton );
|
Rectangle maximizeButtonBounds = boundsInWindow( maximizeButton.isVisible() ? maximizeButton : restoreButton );
|
||||||
Rectangle closeButtonBounds = boundsInWindow( closeButton );
|
Rectangle closeButtonBounds = boundsInWindow( closeButton );
|
||||||
|
|
||||||
|
// clear hit-test cache
|
||||||
|
lastCaptionHitTestTime = 0;
|
||||||
|
|
||||||
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight,
|
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight,
|
||||||
hitTestSpots, appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
|
this::captionHitTest, appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
|
||||||
|
|
||||||
debugTitleBarHeight = titleBarHeight;
|
debugTitleBarHeight = titleBarHeight;
|
||||||
debugHitTestSpots = hitTestSpots;
|
|
||||||
debugAppIconBounds = appIconBounds;
|
debugAppIconBounds = appIconBounds;
|
||||||
debugMinimizeButtonBounds = minimizeButtonBounds;
|
debugMinimizeButtonBounds = minimizeButtonBounds;
|
||||||
debugMaximizeButtonBounds = maximizeButtonBounds;
|
debugMaximizeButtonBounds = maximizeButtonBounds;
|
||||||
@@ -1013,18 +1052,111 @@ public class FlatTitlePane
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Rectangle getNativeHitTestSpot( JComponent c ) {
|
/**
|
||||||
Dimension size = c.getSize();
|
* Returns whether there is a component at the given location, that processes
|
||||||
if( size.width <= 0 || size.height <= 0 )
|
* mouse events. E.g. buttons, menus, etc.
|
||||||
return null;
|
* <p>
|
||||||
|
* Note:
|
||||||
Point location = SwingUtilities.convertPoint( c, 0, 0, window );
|
* <ul>
|
||||||
Rectangle r = new Rectangle( location, size );
|
* <li>This method is invoked often when mouse is moved over title bar
|
||||||
return r;
|
* and should therefore return quickly.
|
||||||
|
* <li>This method is invoked on 'AWT-Windows' thread (not 'AWT-EventQueue' thread)
|
||||||
|
* while processing Windows messages.
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
private boolean captionHitTest( Point pt ) {
|
||||||
|
// Windows invokes this method every ~200ms, even if the mouse has not moved
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
if( pt.x == lastCaptionHitTestX && pt.y == lastCaptionHitTestY && time < lastCaptionHitTestTime + 300 ) {
|
||||||
|
lastCaptionHitTestTime = time;
|
||||||
|
return lastCaptionHitTestResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert pt from window coordinates to layeredPane coordinates
|
||||||
|
Component layeredPane = rootPane.getLayeredPane();
|
||||||
|
int x = pt.x;
|
||||||
|
int y = pt.y;
|
||||||
|
for( Component c = layeredPane; c != window && c != null; c = c.getParent() ) {
|
||||||
|
x -= c.getX();
|
||||||
|
y -= c.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCaptionHitTestX = pt.x;
|
||||||
|
lastCaptionHitTestY = pt.y;
|
||||||
|
lastCaptionHitTestTime = time;
|
||||||
|
lastCaptionHitTestResult = isTitleBarCaptionAt( layeredPane, x, y );
|
||||||
|
return lastCaptionHitTestResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTitleBarCaptionAt( Component c, int x, int y ) {
|
||||||
|
if( !c.isDisplayable() || !c.isVisible() || !contains( c, x, y ) || c == mouseLayer )
|
||||||
|
return true; // continue checking with next component
|
||||||
|
|
||||||
|
if( c.isEnabled() &&
|
||||||
|
(c.getMouseListeners().length > 0 ||
|
||||||
|
c.getMouseMotionListeners().length > 0) )
|
||||||
|
{
|
||||||
|
if( !(c instanceof JComponent) )
|
||||||
|
return false; // assume that this is not a caption because the component has mouse listeners
|
||||||
|
|
||||||
|
// check client property boolean value
|
||||||
|
Object caption = ((JComponent)c).getClientProperty( COMPONENT_TITLE_BAR_CAPTION );
|
||||||
|
if( caption instanceof Boolean )
|
||||||
|
return (boolean) caption;
|
||||||
|
|
||||||
|
// if component is not fully layouted, do not invoke function
|
||||||
|
// because it is too dangerous that the function tries to layout the component,
|
||||||
|
// which could cause a dead lock
|
||||||
|
if( !c.isValid() )
|
||||||
|
return false; // assume that this is not a caption because the component has mouse listeners
|
||||||
|
|
||||||
|
if( caption instanceof Function ) {
|
||||||
|
// check client property function value
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
Function<Point, Boolean> hitTest = (Function<Point, Boolean>) caption;
|
||||||
|
Boolean result = hitTest.apply( new Point( x, y ) );
|
||||||
|
if( result != null )
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
// check component UI
|
||||||
|
ComponentUI ui = JavaCompatibility2.getUI( (JComponent) c );
|
||||||
|
if( !(ui instanceof TitleBarCaptionHitTest) )
|
||||||
|
return false; // assume that this is not a caption because the component has mouse listeners
|
||||||
|
|
||||||
|
Boolean result = ((TitleBarCaptionHitTest)ui).isTitleBarCaptionAt( x, y );
|
||||||
|
if( result != null )
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else continue checking children
|
||||||
|
}
|
||||||
|
|
||||||
|
// check children
|
||||||
|
if( c instanceof Container ) {
|
||||||
|
for( Component child : ((Container)c).getComponents() ) {
|
||||||
|
if( !isTitleBarCaptionAt( child, x - child.getX(), y - child.getY() ) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link Component#contains(int, int)}, but not using that method
|
||||||
|
* because it may be overridden by custom components and invoke code that
|
||||||
|
* tries to request AWT tree lock on 'AWT-Windows' thread.
|
||||||
|
* This could freeze the application if AWT tree is already locked on 'AWT-EventQueue' thread.
|
||||||
|
*/
|
||||||
|
private boolean contains( Component c, int x, int y ) {
|
||||||
|
return x >= 0 && y >= 0 && x < c.getWidth() && y < c.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int lastCaptionHitTestX;
|
||||||
|
private int lastCaptionHitTestY;
|
||||||
|
private long lastCaptionHitTestTime;
|
||||||
|
private boolean lastCaptionHitTestResult;
|
||||||
|
|
||||||
private int debugTitleBarHeight;
|
private int debugTitleBarHeight;
|
||||||
private List<Rectangle> debugHitTestSpots;
|
|
||||||
private Rectangle debugAppIconBounds;
|
private Rectangle debugAppIconBounds;
|
||||||
private Rectangle debugMinimizeButtonBounds;
|
private Rectangle debugMinimizeButtonBounds;
|
||||||
private Rectangle debugMaximizeButtonBounds;
|
private Rectangle debugMaximizeButtonBounds;
|
||||||
@@ -1047,7 +1179,7 @@ public class FlatTitlePane
|
|||||||
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
|
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
|
||||||
insets.bottom += UIScale.scale( 1 );
|
insets.bottom += UIScale.scale( 1 );
|
||||||
|
|
||||||
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
|
if( isWindowTopBorderNeeded() && !isWindowMaximized() )
|
||||||
insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() );
|
insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() );
|
||||||
|
|
||||||
return insets;
|
return insets;
|
||||||
@@ -1066,7 +1198,7 @@ public class FlatTitlePane
|
|||||||
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
|
if( isWindowTopBorderNeeded() && !isWindowMaximized() && !isFullWindowContent() )
|
||||||
WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
|
WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1122,7 +1254,7 @@ public class FlatTitlePane
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute icon width and gap (if icon is show besides the title)
|
// compute icon width and gap (if icon is shown besides the title)
|
||||||
int iconTextGap = 0;
|
int iconTextGap = 0;
|
||||||
int iconWidthAndGap = 0;
|
int iconWidthAndGap = 0;
|
||||||
if( icon != null ) {
|
if( icon != null ) {
|
||||||
@@ -1131,7 +1263,7 @@ public class FlatTitlePane
|
|||||||
iconWidthAndGap = icon.getIconWidth() + iconTextGap;
|
iconWidthAndGap = icon.getIconWidth() + iconTextGap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// layout title and icon (if show besides the title)
|
// layout title and icon (if shown besides the title)
|
||||||
String clippedText = SwingUtilities.layoutCompoundLabel( label, fontMetrics, text, icon,
|
String clippedText = SwingUtilities.layoutCompoundLabel( label, fontMetrics, text, icon,
|
||||||
label.getVerticalAlignment(), label.getHorizontalAlignment(),
|
label.getVerticalAlignment(), label.getHorizontalAlignment(),
|
||||||
label.getVerticalTextPosition(), label.getHorizontalTextPosition(),
|
label.getVerticalTextPosition(), label.getHorizontalTextPosition(),
|
||||||
@@ -1230,10 +1362,7 @@ public class FlatTitlePane
|
|||||||
activeChanged( true );
|
activeChanged( true );
|
||||||
updateNativeTitleBarHeightAndHitTestSpots();
|
updateNativeTitleBarHeightAndHitTestSpots();
|
||||||
|
|
||||||
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() )
|
repaintBorder();
|
||||||
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
|
|
||||||
|
|
||||||
repaintWindowBorder();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1241,10 +1370,22 @@ public class FlatTitlePane
|
|||||||
activeChanged( false );
|
activeChanged( false );
|
||||||
updateNativeTitleBarHeightAndHitTestSpots();
|
updateNativeTitleBarHeightAndHitTestSpots();
|
||||||
|
|
||||||
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() )
|
repaintBorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintBorder() {
|
||||||
|
// Windows 10 top border
|
||||||
|
if( windowTopBorderLayer != null && windowTopBorderLayer.isShowing())
|
||||||
|
WindowTopBorder.getInstance().repaintBorder( windowTopBorderLayer );
|
||||||
|
else if( isWindowTopBorderNeeded() && !isWindowMaximized() && !isFullWindowContent() )
|
||||||
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
|
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
|
||||||
|
|
||||||
repaintWindowBorder();
|
// Window border used for non-native window decorations
|
||||||
|
if( rootPane.getBorder() instanceof FlatRootPaneUI.FlatWindowBorder ) {
|
||||||
|
// not repainting four areas on the four sides because RepaintManager
|
||||||
|
// unions dirty regions, which also results in repaint of whole rootpane
|
||||||
|
rootPane.repaint();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1270,7 +1411,7 @@ debug*/
|
|||||||
public void mouseClicked( MouseEvent e ) {
|
public void mouseClicked( MouseEvent e ) {
|
||||||
// on Linux, when using native library, the mouse clicked event
|
// on Linux, when using native library, the mouse clicked event
|
||||||
// is usually not sent and maximize/restore is done in mouse pressed event
|
// is usually not sent and maximize/restore is done in mouse pressed event
|
||||||
// this check is here for the case that a mouse clicked event comes thru for some reason
|
// this check is here for the case that a mouse clicked event comes through for some reason
|
||||||
if( linuxNativeMove && SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) {
|
if( linuxNativeMove && SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) {
|
||||||
// see comment in mousePressed()
|
// see comment in mousePressed()
|
||||||
if( lastSingleClickWhen != 0 && (e.getWhen() - lastSingleClickWhen) <= getMultiClickInterval() ) {
|
if( lastSingleClickWhen != 0 && (e.getWhen() - lastSingleClickWhen) <= getMultiClickInterval() ) {
|
||||||
@@ -1281,7 +1422,7 @@ debug*/
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton( e ) ) {
|
if( e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton( e ) ) {
|
||||||
if( e.getSource() == iconLabel ) {
|
if( SwingUtilities.getDeepestComponentAt( FlatTitlePane.this, e.getX(), e.getY() ) == iconLabel ) {
|
||||||
// double-click on icon closes window
|
// double-click on icon closes window
|
||||||
close();
|
close();
|
||||||
} else if( !hasNativeCustomDecoration() ) {
|
} else if( !hasNativeCustomDecoration() ) {
|
||||||
@@ -1308,7 +1449,7 @@ debug*/
|
|||||||
if( !SwingUtilities.isLeftMouseButton( e ) )
|
if( !SwingUtilities.isLeftMouseButton( e ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window );
|
dragOffset = SwingUtilities.convertPoint( mouseLayer, e.getPoint(), window );
|
||||||
linuxNativeMove = false;
|
linuxNativeMove = false;
|
||||||
|
|
||||||
// on Linux, move or maximize/restore window
|
// on Linux, move or maximize/restore window
|
||||||
@@ -1421,4 +1562,27 @@ debug*/
|
|||||||
@Override public void componentMoved( ComponentEvent e ) {}
|
@Override public void componentMoved( ComponentEvent e ) {}
|
||||||
@Override public void componentHidden( ComponentEvent e ) {}
|
@Override public void componentHidden( ComponentEvent e ) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface TitleBarCaptionHitTest -----------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For custom components use {@link FlatClientProperties#COMPONENT_TITLE_BAR_CAPTION}
|
||||||
|
* instead of this interface.
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
public interface TitleBarCaptionHitTest {
|
||||||
|
/**
|
||||||
|
* Invoked for a component that is enabled and has mouse listeners,
|
||||||
|
* to check whether it processes mouse input at the given x/y location.
|
||||||
|
* Useful for components that do not use mouse input on whole component bounds.
|
||||||
|
* E.g. a tabbed pane with a few tabs has some empty space beside the tabs
|
||||||
|
* that can be used to move the window.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the component is not interested in mouse input at the given location
|
||||||
|
* {@code false} if the component wants process mouse input at the given location
|
||||||
|
* {@code null} if the component children should be checked
|
||||||
|
*/
|
||||||
|
Boolean isTitleBarCaptionAt( int x, int y );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import javax.swing.*;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,14 +160,14 @@ public class FlatToggleButtonUI
|
|||||||
b.revalidate();
|
b.revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
b.repaint();
|
HiDPIUtils.repaint( b );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TAB_BUTTON_UNDERLINE_PLACEMENT:
|
case TAB_BUTTON_UNDERLINE_PLACEMENT:
|
||||||
case TAB_BUTTON_UNDERLINE_HEIGHT:
|
case TAB_BUTTON_UNDERLINE_HEIGHT:
|
||||||
case TAB_BUTTON_UNDERLINE_COLOR:
|
case TAB_BUTTON_UNDERLINE_COLOR:
|
||||||
case TAB_BUTTON_SELECTED_BACKGROUND:
|
case TAB_BUTTON_SELECTED_BACKGROUND:
|
||||||
b.repaint();
|
HiDPIUtils.repaint( b );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
@@ -35,6 +36,7 @@ import javax.swing.plaf.basic.BasicToolBarSeparatorUI;
|
|||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,7 +132,7 @@ public class FlatToolBarSeparatorUI
|
|||||||
} else
|
} else
|
||||||
installStyle( s );
|
installStyle( s );
|
||||||
s.revalidate();
|
s.revalidate();
|
||||||
s.repaint();
|
HiDPIUtils.repaint( s );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,6 +175,12 @@ public class FlatToolBarSeparatorUI
|
|||||||
if( size != null )
|
if( size != null )
|
||||||
return scale( size );
|
return scale( size );
|
||||||
|
|
||||||
|
// get separator width
|
||||||
|
int separatorWidth = this.separatorWidth;
|
||||||
|
FlatToolBarUI toolBarUI = getToolBarUI( c );
|
||||||
|
if( toolBarUI != null && toolBarUI.separatorWidth != null )
|
||||||
|
separatorWidth = toolBarUI.separatorWidth;
|
||||||
|
|
||||||
// make sure that gap on left and right side of line have same size
|
// make sure that gap on left and right side of line have same size
|
||||||
int sepWidth = (scale( (separatorWidth - LINE_WIDTH) / 2 ) * 2) + scale( LINE_WIDTH );
|
int sepWidth = (scale( (separatorWidth - LINE_WIDTH) / 2 ) * 2) + scale( LINE_WIDTH );
|
||||||
|
|
||||||
@@ -196,6 +204,12 @@ public class FlatToolBarSeparatorUI
|
|||||||
float lineWidth = scale( 1f );
|
float lineWidth = scale( 1f );
|
||||||
float offset = scale( 2f );
|
float offset = scale( 2f );
|
||||||
|
|
||||||
|
// get separator color
|
||||||
|
Color separatorColor = this.separatorColor;
|
||||||
|
FlatToolBarUI toolBarUI = getToolBarUI( c );
|
||||||
|
if( toolBarUI != null && toolBarUI.separatorColor != null )
|
||||||
|
separatorColor = toolBarUI.separatorColor;
|
||||||
|
|
||||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
g.setColor( separatorColor );
|
g.setColor( separatorColor );
|
||||||
|
|
||||||
@@ -210,4 +224,11 @@ public class FlatToolBarSeparatorUI
|
|||||||
private boolean isVertical( JComponent c ) {
|
private boolean isVertical( JComponent c ) {
|
||||||
return ((JToolBar.Separator)c).getOrientation() == SwingConstants.VERTICAL;
|
return ((JToolBar.Separator)c).getOrientation() == SwingConstants.VERTICAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FlatToolBarUI getToolBarUI( JComponent c ) {
|
||||||
|
Container parent = c.getParent();
|
||||||
|
return (parent instanceof JToolBar && ((JToolBar)parent).getUI() instanceof FlatToolBarUI)
|
||||||
|
? (FlatToolBarUI) ((JToolBar)parent).getUI()
|
||||||
|
: null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import javax.swing.plaf.basic.BasicToolBarUI;
|
|||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -82,7 +83,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatToolBarUI
|
public class FlatToolBarUI
|
||||||
extends BasicToolBarUI
|
extends BasicToolBarUI
|
||||||
implements StyleableUI
|
implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
|
||||||
{
|
{
|
||||||
/** @since 1.4 */ @Styleable protected boolean focusableButtons;
|
/** @since 1.4 */ @Styleable protected boolean focusableButtons;
|
||||||
/** @since 2 */ @Styleable protected boolean arrowKeysOnlyNavigation;
|
/** @since 2 */ @Styleable protected boolean arrowKeysOnlyNavigation;
|
||||||
@@ -93,6 +94,10 @@ public class FlatToolBarUI
|
|||||||
@Styleable protected Insets borderMargins;
|
@Styleable protected Insets borderMargins;
|
||||||
@Styleable protected Color gripColor;
|
@Styleable protected Color gripColor;
|
||||||
|
|
||||||
|
// for FlatToolBarSeparatorUI
|
||||||
|
/** @since 3.3 */ @Styleable protected Integer separatorWidth;
|
||||||
|
/** @since 3.3 */ @Styleable protected Color separatorColor;
|
||||||
|
|
||||||
private FocusTraversalPolicy focusTraversalPolicy;
|
private FocusTraversalPolicy focusTraversalPolicy;
|
||||||
private Boolean oldFloatable;
|
private Boolean oldFloatable;
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
@@ -439,7 +444,7 @@ public class FlatToolBarUI
|
|||||||
|
|
||||||
// repaint button group
|
// repaint button group
|
||||||
if( gr != null )
|
if( gr != null )
|
||||||
toolBar.repaint( gr );
|
HiDPIUtils.repaint(toolBar, gr );
|
||||||
}
|
}
|
||||||
|
|
||||||
private ButtonGroup getButtonGroup( AbstractButton b ) {
|
private ButtonGroup getButtonGroup( AbstractButton b ) {
|
||||||
@@ -449,6 +454,15 @@ public class FlatToolBarUI
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
|
||||||
|
|
||||||
|
/** @since 3.4 */
|
||||||
|
@Override
|
||||||
|
public Boolean isTitleBarCaptionAt( int x, int y ) {
|
||||||
|
// necessary because BasicToolBarUI adds some mouse listeners for dragging when toolbar is floatable
|
||||||
|
return null; // check children
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatToolBarFocusTraversalPolicy ------------------------------
|
//---- class FlatToolBarFocusTraversalPolicy ------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -517,8 +531,11 @@ public class FlatToolBarUI
|
|||||||
|
|
||||||
private Component getRecentComponent( Container aContainer, boolean first ) {
|
private Component getRecentComponent( Container aContainer, boolean first ) {
|
||||||
// if moving focus into the toolbar, focus recently focused toolbar button
|
// if moving focus into the toolbar, focus recently focused toolbar button
|
||||||
if( focusedCompIndex >= 0 && focusedCompIndex < toolBar.getComponentCount() )
|
if( focusedCompIndex >= 0 && focusedCompIndex < toolBar.getComponentCount() ) {
|
||||||
return toolBar.getComponent( focusedCompIndex );
|
Component c = toolBar.getComponent( focusedCompIndex );
|
||||||
|
if( accept( c ) )
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
return first
|
return first
|
||||||
? super.getFirstComponent( aContainer )
|
? super.getFirstComponent( aContainer )
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public class FlatToolTipUI
|
|||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
// update HTML renderer if necessary
|
// update HTML renderer if necessary
|
||||||
FlatLabelUI.updateHTMLRenderer( c, ((JToolTip)c).getTipText(), false );
|
FlatHTML.updateRendererCSSFontBaseSize( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,11 +81,7 @@ public class FlatToolTipUI
|
|||||||
/** @since 2.0.1 */
|
/** @since 2.0.1 */
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
String name = e.getPropertyName();
|
FlatHTML.propertyChange( e );
|
||||||
if( name == "tiptext" || name == "font" || name == "foreground" ) {
|
|
||||||
JToolTip toolTip = (JToolTip) e.getSource();
|
|
||||||
FlatLabelUI.updateHTMLRenderer( toolTip, toolTip.getTipText(), false );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import javax.swing.tree.DefaultTreeCellRenderer;
|
|||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -310,7 +311,7 @@ public class FlatTreeUI
|
|||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case TREE_WIDE_SELECTION:
|
case TREE_WIDE_SELECTION:
|
||||||
case TREE_PAINT_SELECTION:
|
case TREE_PAINT_SELECTION:
|
||||||
tree.repaint();
|
HiDPIUtils.repaint( tree );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "dropLocation":
|
case "dropLocation":
|
||||||
@@ -325,7 +326,7 @@ public class FlatTreeUI
|
|||||||
case STYLE_CLASS:
|
case STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
tree.revalidate();
|
tree.revalidate();
|
||||||
tree.repaint();
|
HiDPIUtils.repaint( tree );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "enabled":
|
case "enabled":
|
||||||
@@ -353,7 +354,7 @@ public class FlatTreeUI
|
|||||||
|
|
||||||
Rectangle r = tree.getPathBounds( loc.getPath() );
|
Rectangle r = tree.getPathBounds( loc.getPath() );
|
||||||
if( r != null )
|
if( r != null )
|
||||||
tree.repaint( 0, r.y, tree.getWidth(), r.height );
|
HiDPIUtils.repaint( tree, 0, r.y, tree.getWidth(), r.height );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -370,14 +371,14 @@ public class FlatTreeUI
|
|||||||
{
|
{
|
||||||
if( changedPaths.length > 4 ) {
|
if( changedPaths.length > 4 ) {
|
||||||
// same is done in BasicTreeUI.Handler.valueChanged()
|
// same is done in BasicTreeUI.Handler.valueChanged()
|
||||||
tree.repaint();
|
HiDPIUtils.repaint( tree );
|
||||||
} else {
|
} else {
|
||||||
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
|
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
|
||||||
|
|
||||||
for( TreePath path : changedPaths ) {
|
for( TreePath path : changedPaths ) {
|
||||||
Rectangle r = getPathBounds( tree, path );
|
Rectangle r = getPathBounds( tree, path );
|
||||||
if( r != null )
|
if( r != null )
|
||||||
tree.repaint( r.x, r.y - arc, r.width, r.height + (arc * 2) );
|
HiDPIUtils.repaint( tree, r.x, r.y - arc, r.width, r.height + (arc * 2) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import javax.swing.Action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for UI actions used in ActionMap.
|
||||||
|
* (similar to class sun.swing.UIAction)
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
public abstract class FlatUIAction
|
||||||
|
implements Action
|
||||||
|
{
|
||||||
|
protected final String name;
|
||||||
|
protected final Action delegate;
|
||||||
|
|
||||||
|
protected FlatUIAction( String name ) {
|
||||||
|
this.name = name;
|
||||||
|
this.delegate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FlatUIAction( Action delegate ) {
|
||||||
|
this.name = null;
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue( String key ) {
|
||||||
|
if( key == NAME && delegate == null )
|
||||||
|
return name;
|
||||||
|
return (delegate != null) ? delegate.getValue( key ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return (delegate != null) ? delegate.isEnabled() : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do nothing in following methods because this class is immutable
|
||||||
|
@Override public void putValue( String key, Object value ) {}
|
||||||
|
@Override public void setEnabled( boolean b ) {}
|
||||||
|
@Override public void addPropertyChangeListener( PropertyChangeListener listener ) {}
|
||||||
|
@Override public void removePropertyChangeListener( PropertyChangeListener listener ) {}
|
||||||
|
}
|
||||||
@@ -60,6 +60,7 @@ import javax.swing.border.Border;
|
|||||||
import javax.swing.border.CompoundBorder;
|
import javax.swing.border.CompoundBorder;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
|
import javax.swing.tree.DefaultTreeCellEditor;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatIntelliJLaf;
|
import com.formdev.flatlaf.FlatIntelliJLaf;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
@@ -123,6 +124,11 @@ public class FlatUIUtils
|
|||||||
dest.right = src.right;
|
dest.right = src.right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.5 */
|
||||||
|
public static boolean isInsetsEmpty( Insets insets ) {
|
||||||
|
return insets.top == 0 && insets.left == 0 && insets.bottom == 0 && insets.right == 0;
|
||||||
|
}
|
||||||
|
|
||||||
public static Color getUIColor( String key, int defaultColorRGB ) {
|
public static Color getUIColor( String key, int defaultColorRGB ) {
|
||||||
Color color = UIManager.getColor( key );
|
Color color = UIManager.getColor( key );
|
||||||
return (color != null) ? color : new Color( defaultColorRGB );
|
return (color != null) ? color : new Color( defaultColorRGB );
|
||||||
@@ -299,15 +305,14 @@ public class FlatUIUtils
|
|||||||
if( c == null )
|
if( c == null )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// check whether used in cell editor (check 3 levels up)
|
// check whether used as table cell editor
|
||||||
Component c2 = c;
|
Container parent = c.getParent();
|
||||||
for( int i = 0; i <= 2 && c2 != null; i++ ) {
|
if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c )
|
||||||
Container parent = c2.getParent();
|
|
||||||
if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c2 )
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
c2 = parent;
|
// check whether used as tree cell editor
|
||||||
}
|
if( parent instanceof DefaultTreeCellEditor.EditorContainer )
|
||||||
|
return true;
|
||||||
|
|
||||||
// check whether used as cell editor
|
// check whether used as cell editor
|
||||||
// Table.editor is set in JTable.GenericEditor constructor
|
// Table.editor is set in JTable.GenericEditor constructor
|
||||||
@@ -601,28 +606,55 @@ public class FlatUIUtils
|
|||||||
public static void paintOutlinedComponent( Graphics2D g, int x, int y, int width, int height,
|
public static void paintOutlinedComponent( Graphics2D g, int x, int y, int width, int height,
|
||||||
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
|
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
|
||||||
Paint focusColor, Paint borderColor, Paint background )
|
Paint focusColor, Paint borderColor, Paint background )
|
||||||
|
{
|
||||||
|
paintOutlinedComponent( g, x, y, width, height, focusWidth, focusWidthFraction, focusInnerWidth,
|
||||||
|
borderWidth, arc, focusColor, borderColor, background, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void paintOutlinedComponent( Graphics2D g, int x, int y, int width, int height,
|
||||||
|
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
|
||||||
|
Paint focusColor, Paint borderColor, Paint background, boolean scrollPane )
|
||||||
{
|
{
|
||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
|
||||||
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
if( (int) systemScaleFactor != systemScaleFactor ) {
|
||||||
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
||||||
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
|
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
|
||||||
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||||
paintOutlinedComponentImpl( g2d, x2, y2, width2, height2,
|
paintOutlinedComponentImpl( g2d, x2, y2, width2, height2,
|
||||||
(float) (focusWidth * scaleFactor), focusWidthFraction, (float) (focusInnerWidth * scaleFactor),
|
(float) (focusWidth * scaleFactor), focusWidthFraction, (float) (focusInnerWidth * scaleFactor),
|
||||||
(float) (borderWidth * scaleFactor), (float) (arc * scaleFactor),
|
(float) (borderWidth * scaleFactor), (float) (arc * scaleFactor),
|
||||||
focusColor, borderColor, background );
|
focusColor, borderColor, background, scrollPane, scaleFactor );
|
||||||
} );
|
} );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
paintOutlinedComponentImpl( g, x, y, width, height, focusWidth, focusWidthFraction, focusInnerWidth,
|
paintOutlinedComponentImpl( g, x, y, width, height, focusWidth, focusWidthFraction, focusInnerWidth,
|
||||||
borderWidth, arc, focusColor, borderColor, background );
|
borderWidth, arc, focusColor, borderColor, background, scrollPane, systemScaleFactor );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings( "SelfAssignment" ) // Error Prone
|
||||||
private static void paintOutlinedComponentImpl( Graphics2D g, int x, int y, int width, int height,
|
private static void paintOutlinedComponentImpl( Graphics2D g, int x, int y, int width, int height,
|
||||||
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
|
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
|
||||||
Paint focusColor, Paint borderColor, Paint background )
|
Paint focusColor, Paint borderColor, Paint background, boolean scrollPane, double scaleFactor )
|
||||||
{
|
{
|
||||||
|
// Special handling for scrollpane and fractional scale factors (e.g. 1.25 - 1.75),
|
||||||
|
// where Swing scales one "logical" pixel (border insets) to either one or two physical pixels.
|
||||||
|
// Antialiasing is used to paint the border, which usually needs two physical pixels
|
||||||
|
// at small scale factors. 1px for the solid border and another 1px for antialiasing.
|
||||||
|
// But scrollpane view is painted over the border, which results in a painted border
|
||||||
|
// that is 1px thick at some sides and 2px thick at other sides.
|
||||||
|
if( scrollPane && scaleFactor != (int) scaleFactor ) {
|
||||||
|
if( focusWidth > 0 ) {
|
||||||
|
// reduce outer border thickness (focusWidth) so that inner side of
|
||||||
|
// component border (focusWidth + borderWidth) is at a full pixel
|
||||||
|
int totalWidth = (int) (focusWidth + borderWidth);
|
||||||
|
focusWidth = totalWidth - borderWidth;
|
||||||
|
} else {// if( scaleFactor > 1 && scaleFactor < 2 ) {
|
||||||
|
// reduce component border thickness (borderWidth) to full pixels
|
||||||
|
borderWidth = (int) borderWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// outside bounds of the border and the background
|
// outside bounds of the border and the background
|
||||||
float x1 = x + focusWidth;
|
float x1 = x + focusWidth;
|
||||||
float y1 = y + focusWidth;
|
float y1 = y + focusWidth;
|
||||||
@@ -734,7 +766,7 @@ public class FlatUIUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a (rounded) rectangle used to paint components (border, background, etc).
|
* Creates a (rounded) rectangle used to paint components (border, background, etc.).
|
||||||
* The given arc diameter is limited to min(width,height).
|
* The given arc diameter is limited to min(width,height).
|
||||||
*/
|
*/
|
||||||
public static Shape createComponentRectangle( float x, float y, float w, float h, float arc ) {
|
public static Shape createComponentRectangle( float x, float y, float w, float h, float arc ) {
|
||||||
@@ -780,7 +812,7 @@ public class FlatUIUtils
|
|||||||
|
|
||||||
if( arcTopLeft > 0 || arcTopRight > 0 || arcBottomLeft > 0 || arcBottomRight > 0 ) {
|
if( arcTopLeft > 0 || arcTopRight > 0 || arcBottomLeft > 0 || arcBottomRight > 0 ) {
|
||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
|
||||||
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
if( systemScaleFactor != (int) systemScaleFactor ) {
|
||||||
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
||||||
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
|
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
|
||||||
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||||
@@ -1315,13 +1347,13 @@ debug*/
|
|||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
|
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
|
||||||
repaintComponent.repaint();
|
HiDPIUtils.repaint( repaintComponent );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
|
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
|
||||||
repaintComponent.repaint();
|
HiDPIUtils.repaint( repaintComponent );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
@@ -48,14 +47,9 @@ public class FlatViewportUI
|
|||||||
|
|
||||||
Component view = ((JViewport)c).getView();
|
Component view = ((JViewport)c).getView();
|
||||||
if( view instanceof JComponent ) {
|
if( view instanceof JComponent ) {
|
||||||
try {
|
ComponentUI ui = JavaCompatibility2.getUI( (JComponent) view );
|
||||||
Method m = view.getClass().getMethod( "getUI" );
|
|
||||||
Object ui = m.invoke( view );
|
|
||||||
if( ui instanceof ViewportPainter )
|
if( ui instanceof ViewportPainter )
|
||||||
((ViewportPainter)ui).paintViewport( g, (JComponent) view, (JViewport) c );
|
((ViewportPainter)ui).paintViewport( g, (JComponent) view, (JViewport) c );
|
||||||
} catch( Exception ex ) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ import java.beans.PropertyChangeEvent;
|
|||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.Timer;
|
import javax.swing.Timer;
|
||||||
@@ -89,7 +89,7 @@ class FlatWindowsNativeWindowBorder
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
// check whether native library was successfully loaded
|
// check whether native library was successfully loaded
|
||||||
if( !FlatNativeLibrary.isLoaded() )
|
if( !FlatNativeWindowsLibrary.isLoaded() )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// create new instance
|
// create new instance
|
||||||
@@ -159,7 +159,7 @@ class FlatWindowsNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
|
public void updateTitleBarInfo( Window window, int titleBarHeight, Predicate<Point> captionHitTestCallback,
|
||||||
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
|
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
|
||||||
Rectangle closeButtonBounds )
|
Rectangle closeButtonBounds )
|
||||||
{
|
{
|
||||||
@@ -168,7 +168,7 @@ class FlatWindowsNativeWindowBorder
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
wndProc.titleBarHeight = titleBarHeight;
|
wndProc.titleBarHeight = titleBarHeight;
|
||||||
wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] );
|
wndProc.captionHitTestCallback = captionHitTestCallback;
|
||||||
wndProc.appIconBounds = cloneRectange( appIconBounds );
|
wndProc.appIconBounds = cloneRectange( appIconBounds );
|
||||||
wndProc.minimizeButtonBounds = cloneRectange( minimizeButtonBounds );
|
wndProc.minimizeButtonBounds = cloneRectange( minimizeButtonBounds );
|
||||||
wndProc.maximizeButtonBounds = cloneRectange( maximizeButtonBounds );
|
wndProc.maximizeButtonBounds = cloneRectange( maximizeButtonBounds );
|
||||||
@@ -288,8 +288,8 @@ class FlatWindowsNativeWindowBorder
|
|||||||
private final long hwnd;
|
private final long hwnd;
|
||||||
|
|
||||||
// Swing coordinates/values may be scaled on a HiDPI screen
|
// Swing coordinates/values may be scaled on a HiDPI screen
|
||||||
private int titleBarHeight;
|
private int titleBarHeight; // measured from window top edge, which may be out-of-screen if maximized
|
||||||
private Rectangle[] hitTestSpots;
|
private Predicate<Point> captionHitTestCallback;
|
||||||
private Rectangle appIconBounds;
|
private Rectangle appIconBounds;
|
||||||
private Rectangle minimizeButtonBounds;
|
private Rectangle minimizeButtonBounds;
|
||||||
private Rectangle maximizeButtonBounds;
|
private Rectangle maximizeButtonBounds;
|
||||||
@@ -340,50 +340,61 @@ class FlatWindowsNativeWindowBorder
|
|||||||
private int onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
|
private int onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
|
||||||
// scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen
|
// scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen
|
||||||
Point pt = scaleDown( x, y );
|
Point pt = scaleDown( x, y );
|
||||||
int sx = pt.x;
|
|
||||||
int sy = pt.y;
|
|
||||||
|
|
||||||
// return HTSYSMENU if mouse is over application icon
|
// return HTSYSMENU if mouse is over application icon
|
||||||
// - left-click on HTSYSMENU area shows system menu
|
// - left-click on HTSYSMENU area shows system menu
|
||||||
// - double-left-click sends WM_CLOSE
|
// - double-left-click sends WM_CLOSE
|
||||||
if( contains( appIconBounds, sx, sy ) )
|
if( contains( appIconBounds, pt ) )
|
||||||
return HTSYSMENU;
|
return HTSYSMENU;
|
||||||
|
|
||||||
// return HTMINBUTTON if mouse is over minimize button
|
// return HTMINBUTTON if mouse is over minimize button
|
||||||
// - hovering mouse over HTMINBUTTON area shows tooltip on Windows 10/11
|
// - hovering mouse over HTMINBUTTON area shows tooltip on Windows 10/11
|
||||||
if( contains( minimizeButtonBounds, sx, sy ) )
|
if( contains( minimizeButtonBounds, pt ) )
|
||||||
return HTMINBUTTON;
|
return HTMINBUTTON;
|
||||||
|
|
||||||
// return HTMAXBUTTON if mouse is over maximize/restore button
|
// return HTMAXBUTTON if mouse is over maximize/restore button
|
||||||
// - hovering mouse over HTMAXBUTTON area shows tooltip on Windows 10
|
// - hovering mouse over HTMAXBUTTON area shows tooltip on Windows 10
|
||||||
// - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11
|
// - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11
|
||||||
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
|
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
|
||||||
if( contains( maximizeButtonBounds, sx, sy ) )
|
if( contains( maximizeButtonBounds, pt ) )
|
||||||
return HTMAXBUTTON;
|
return HTMAXBUTTON;
|
||||||
|
|
||||||
// return HTCLOSE if mouse is over close button
|
// return HTCLOSE if mouse is over close button
|
||||||
// - hovering mouse over HTCLOSE area shows tooltip on Windows 10/11
|
// - hovering mouse over HTCLOSE area shows tooltip on Windows 10/11
|
||||||
if( contains( closeButtonBounds, sx, sy ) )
|
if( contains( closeButtonBounds, pt ) )
|
||||||
return HTCLOSE;
|
return HTCLOSE;
|
||||||
|
|
||||||
boolean isOnTitleBar = (sy < titleBarHeight);
|
// return HTTOP if mouse is over top resize border
|
||||||
|
// - hovering mouse shows vertical resize cursor
|
||||||
|
// - left-click and drag vertically resizes window
|
||||||
|
if( isOnResizeBorder )
|
||||||
|
return HTTOP;
|
||||||
|
|
||||||
|
boolean isOnTitleBar = (pt.y < titleBarHeight);
|
||||||
if( isOnTitleBar ) {
|
if( isOnTitleBar ) {
|
||||||
// use a second reference to the array to avoid that it can be changed
|
// return HTCLIENT if mouse is over any Swing component in title bar
|
||||||
// in another thread while processing the array
|
// that processes mouse events (e.g. buttons, menus, etc)
|
||||||
Rectangle[] hitTestSpots2 = hitTestSpots;
|
// - Windows ignores mouse events in this area
|
||||||
for( Rectangle spot : hitTestSpots2 ) {
|
try {
|
||||||
if( spot.contains( sx, sy ) )
|
if( captionHitTestCallback != null && !captionHitTestCallback.test( pt ) )
|
||||||
|
return HTCLIENT;
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
// return HTCAPTION if mouse is over title bar
|
||||||
|
// - right-click shows system menu
|
||||||
|
// - double-left-click maximizes/restores window size
|
||||||
|
return HTCAPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return HTCLIENT
|
||||||
|
// - Windows ignores mouse events in this area
|
||||||
return HTCLIENT;
|
return HTCLIENT;
|
||||||
}
|
}
|
||||||
return isOnResizeBorder ? HTTOP : HTCAPTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isOnResizeBorder ? HTTOP : HTCLIENT;
|
private boolean contains( Rectangle rect, Point pt ) {
|
||||||
}
|
return (rect != null && rect.contains( pt ) );
|
||||||
|
|
||||||
private boolean contains( Rectangle rect, int x, int y ) {
|
|
||||||
return (rect != null && rect.contains( x, y ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.event.ComponentAdapter;
|
||||||
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.ComponentListener;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class FullWindowContentSupport
|
||||||
|
{
|
||||||
|
private static final String KEY_DEBUG_SHOW_PLACEHOLDERS = "FlatLaf.debug.panel.showPlaceholders";
|
||||||
|
|
||||||
|
private static ArrayList<WeakReference<JComponent>> placeholders = new ArrayList<>();
|
||||||
|
|
||||||
|
static Dimension getPlaceholderPreferredSize( JComponent c, String options ) {
|
||||||
|
JRootPane rootPane;
|
||||||
|
Rectangle bounds;
|
||||||
|
|
||||||
|
if( !options.startsWith( SystemInfo.isMacOS ? "mac" : "win" ) ||
|
||||||
|
!c.isDisplayable() ||
|
||||||
|
(rootPane = SwingUtilities.getRootPane( c )) == null ||
|
||||||
|
(bounds = (Rectangle) rootPane.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS )) == null )
|
||||||
|
return new Dimension( 0, 0 );
|
||||||
|
|
||||||
|
if( options.length() > 3 ) {
|
||||||
|
if( (options.contains( "leftToRight" ) && !c.getComponentOrientation().isLeftToRight()) ||
|
||||||
|
(options.contains( "rightToLeft" ) && c.getComponentOrientation().isLeftToRight()) )
|
||||||
|
return new Dimension( 0, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// On macOS, the client property is updated very late when toggling full screen,
|
||||||
|
// which results in "jumping" layout after full screen toggle finished.
|
||||||
|
// To avoid that, get up-to-date buttons bounds from macOS.
|
||||||
|
if( SystemInfo.isMacFullWindowContentSupported && FlatNativeMacLibrary.isLoaded() ) {
|
||||||
|
Rectangle r = FlatNativeMacLibrary.getWindowButtonsBounds( SwingUtilities.windowForComponent( c ) );
|
||||||
|
if( r != null )
|
||||||
|
bounds = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = bounds.width;
|
||||||
|
int height = bounds.height;
|
||||||
|
|
||||||
|
if( options.length() > 3 ) {
|
||||||
|
if( width == 0 && options.contains( "zeroInFullScreen" ) )
|
||||||
|
height = 0;
|
||||||
|
|
||||||
|
if( options.contains( "horizontal" ) )
|
||||||
|
height = 0;
|
||||||
|
if( options.contains( "vertical" ) )
|
||||||
|
width = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Dimension( width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void registerPlaceholder( JComponent c ) {
|
||||||
|
synchronized( placeholders ) {
|
||||||
|
if( indexOfPlaceholder( c ) < 0 )
|
||||||
|
placeholders.add( new WeakReference<>( c ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unregisterPlaceholder( JComponent c ) {
|
||||||
|
synchronized( placeholders ) {
|
||||||
|
int index = indexOfPlaceholder( c );
|
||||||
|
if( index >= 0 )
|
||||||
|
placeholders.remove( index );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int indexOfPlaceholder( JComponent c ) {
|
||||||
|
int size = placeholders.size();
|
||||||
|
for( int i = 0; i < size; i++ ) {
|
||||||
|
if( placeholders.get( i ).get() == c )
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void revalidatePlaceholders( Component container ) {
|
||||||
|
synchronized( placeholders ) {
|
||||||
|
if( placeholders.isEmpty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for( Iterator<WeakReference<JComponent>> it = placeholders.iterator(); it.hasNext(); ) {
|
||||||
|
WeakReference<JComponent> ref = it.next();
|
||||||
|
JComponent c = ref.get();
|
||||||
|
|
||||||
|
// remove already released placeholder
|
||||||
|
if( c == null ) {
|
||||||
|
it.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// revalidate placeholder if is in given container
|
||||||
|
if( SwingUtilities.isDescendingFrom( c, container ) )
|
||||||
|
c.revalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ComponentListener macInstallListeners( JRootPane rootPane ) {
|
||||||
|
ComponentListener l = new ComponentAdapter() {
|
||||||
|
boolean lastFullScreen;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentResized( ComponentEvent e ) {
|
||||||
|
Window window = SwingUtilities.windowForComponent( rootPane );
|
||||||
|
if( window == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
boolean fullScreen = FlatNativeMacLibrary.isLoaded() && FlatNativeMacLibrary.isWindowFullScreen( window );
|
||||||
|
if( fullScreen == lastFullScreen )
|
||||||
|
return;
|
||||||
|
|
||||||
|
lastFullScreen = fullScreen;
|
||||||
|
macUpdateFullWindowContentButtonsBoundsProperty( rootPane );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
rootPane.addComponentListener( l );
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void macUninstallListeners( JRootPane rootPane, ComponentListener l ) {
|
||||||
|
if( l != null )
|
||||||
|
rootPane.removeComponentListener( l );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void macUpdateFullWindowContentButtonsBoundsProperty( JRootPane rootPane ) {
|
||||||
|
if( !SystemInfo.isMacFullWindowContentSupported || !rootPane.isDisplayable() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Rectangle bounds = null;
|
||||||
|
if( FlatClientProperties.clientPropertyBoolean( rootPane, "apple.awt.fullWindowContent", false ) ) {
|
||||||
|
bounds = FlatNativeMacLibrary.isLoaded()
|
||||||
|
? FlatNativeMacLibrary.getWindowButtonsBounds( SwingUtilities.windowForComponent( rootPane ) )
|
||||||
|
: new Rectangle( 68, 28 ); // default size
|
||||||
|
}
|
||||||
|
rootPane.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS, bounds );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void macUninstallFullWindowContentButtonsBoundsProperty( JRootPane rootPane ) {
|
||||||
|
if( !SystemInfo.isMacFullWindowContentSupported )
|
||||||
|
return;
|
||||||
|
|
||||||
|
rootPane.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void debugPaint( Graphics g, JComponent c ) {
|
||||||
|
if( !UIManager.getBoolean( KEY_DEBUG_SHOW_PLACEHOLDERS ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
int width = c.getWidth();
|
||||||
|
int height = c.getHeight();
|
||||||
|
if( width <= 0 || height <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// draw red figure
|
||||||
|
g.setColor( Color.red );
|
||||||
|
debugPaintRect( g, new Rectangle( width, height ) );
|
||||||
|
|
||||||
|
// draw magenta figure if buttons bounds are not equal to placeholder bounds
|
||||||
|
JRootPane rootPane;
|
||||||
|
Rectangle bounds;
|
||||||
|
if( (rootPane = SwingUtilities.getRootPane( c )) != null &&
|
||||||
|
(bounds = (Rectangle) rootPane.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS )) != null &&
|
||||||
|
(bounds.width != width || bounds.height != height) )
|
||||||
|
{
|
||||||
|
g.setColor( Color.magenta );
|
||||||
|
debugPaintRect( g, SwingUtilities.convertRectangle( rootPane, bounds, c ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void debugPaintRect( Graphics g, Rectangle r ) {
|
||||||
|
// draw rectangle
|
||||||
|
g.drawRect( r.x, r.y, r.width - 1, r.height - 1 );
|
||||||
|
|
||||||
|
// draw diagonal cross
|
||||||
|
int x2 = r.x + r.width - 1;
|
||||||
|
int y2 = r.y + r.height - 1;
|
||||||
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
g.drawLine( r.x, r.y, x2, y2 );
|
||||||
|
g.drawLine( r.x, y2, x2, r.y );
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,306 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 FormDev Software GmbH
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Container;
|
|
||||||
import java.awt.EventQueue;
|
|
||||||
import java.awt.Graphics;
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.Toolkit;
|
|
||||||
import java.awt.Window;
|
|
||||||
import java.awt.event.HierarchyEvent;
|
|
||||||
import java.awt.event.HierarchyListener;
|
|
||||||
import java.beans.PropertyChangeListener;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.swing.JRootPane;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.UIManager;
|
|
||||||
import javax.swing.plaf.BorderUIResource;
|
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Support for custom window decorations provided by JetBrains Runtime (based on OpenJDK).
|
|
||||||
* Requires that the application runs on Windows 10 in a JetBrains Runtime 11 or later.
|
|
||||||
* <ul>
|
|
||||||
* <li><a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime</a></li>
|
|
||||||
* <li><a href="https://github.com/JetBrains/JetBrainsRuntime">https://github.com/JetBrains/JetBrainsRuntime</a></li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @author Karl Tauber
|
|
||||||
*/
|
|
||||||
public class JBRCustomDecorations
|
|
||||||
{
|
|
||||||
private static Boolean supported;
|
|
||||||
private static Method Window_hasCustomDecoration;
|
|
||||||
private static Method Window_setHasCustomDecoration;
|
|
||||||
private static Method WWindowPeer_setCustomDecorationTitleBarHeight;
|
|
||||||
private static Method WWindowPeer_setCustomDecorationHitTestSpots;
|
|
||||||
private static Method AWTAccessor_getComponentAccessor;
|
|
||||||
private static Method AWTAccessor_ComponentAccessor_getPeer;
|
|
||||||
|
|
||||||
public static boolean isSupported() {
|
|
||||||
initialize();
|
|
||||||
return supported;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Object install( JRootPane rootPane ) {
|
|
||||||
if( !isSupported() )
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// check whether root pane already has a parent, which is the case when switching LaF
|
|
||||||
Container parent = rootPane.getParent();
|
|
||||||
if( parent != null ) {
|
|
||||||
if( parent instanceof Window )
|
|
||||||
FlatNativeWindowBorder.install( (Window) parent );
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use hierarchy listener to wait until the root pane is added to a window.
|
|
||||||
// Enabling JBR decorations must be done very early, probably before
|
|
||||||
// window becomes displayable (window.isDisplayable()). Tried also using
|
|
||||||
// "ancestor" property change event on root pane, but this is invoked too late.
|
|
||||||
HierarchyListener addListener = new HierarchyListener() {
|
|
||||||
@Override
|
|
||||||
public void hierarchyChanged( HierarchyEvent e ) {
|
|
||||||
if( e.getChanged() != rootPane || (e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) == 0 )
|
|
||||||
return;
|
|
||||||
|
|
||||||
Container parent = e.getChangedParent();
|
|
||||||
if( parent instanceof Window )
|
|
||||||
FlatNativeWindowBorder.install( (Window) parent );
|
|
||||||
|
|
||||||
// remove listener since it is actually not possible to uninstall JBR decorations
|
|
||||||
// use invokeLater to remove listener to avoid that listener
|
|
||||||
// is removed while listener queue is processed
|
|
||||||
EventQueue.invokeLater( () -> {
|
|
||||||
rootPane.removeHierarchyListener( this );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
rootPane.addHierarchyListener( addListener );
|
|
||||||
return addListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uninstall( JRootPane rootPane, Object data ) {
|
|
||||||
// remove listener (if not yet done)
|
|
||||||
if( data instanceof HierarchyListener )
|
|
||||||
rootPane.removeHierarchyListener( (HierarchyListener) data );
|
|
||||||
|
|
||||||
// since it is actually not possible to uninstall JBR decorations,
|
|
||||||
// simply reduce titleBarHeight so that it is still possible to resize window
|
|
||||||
// and remove hitTestSpots
|
|
||||||
Container parent = rootPane.getParent();
|
|
||||||
if( parent instanceof Window )
|
|
||||||
setHasCustomDecoration( (Window) parent, false );
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean hasCustomDecoration( Window window ) {
|
|
||||||
if( !isSupported() )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
return (Boolean) Window_hasCustomDecoration.invoke( window );
|
|
||||||
} catch( Exception ex ) {
|
|
||||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
|
|
||||||
if( !isSupported() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if( hasCustomDecoration )
|
|
||||||
Window_setHasCustomDecoration.invoke( window );
|
|
||||||
else
|
|
||||||
setTitleBarHeightAndHitTestSpots( window, 4, Collections.emptyList() );
|
|
||||||
} catch( Exception ex ) {
|
|
||||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, List<Rectangle> hitTestSpots ) {
|
|
||||||
if( !isSupported() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Object compAccessor = AWTAccessor_getComponentAccessor.invoke( null );
|
|
||||||
Object peer = AWTAccessor_ComponentAccessor_getPeer.invoke( compAccessor, window );
|
|
||||||
WWindowPeer_setCustomDecorationTitleBarHeight.invoke( peer, titleBarHeight );
|
|
||||||
WWindowPeer_setCustomDecorationHitTestSpots.invoke( peer, hitTestSpots );
|
|
||||||
} catch( Exception ex ) {
|
|
||||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void initialize() {
|
|
||||||
if( supported != null )
|
|
||||||
return;
|
|
||||||
supported = false;
|
|
||||||
|
|
||||||
// requires JetBrains Runtime 11 and Windows 10
|
|
||||||
if( !SystemInfo.isJetBrainsJVM_11_orLater || !SystemInfo.isWindows_10_orLater )
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Class<?> awtAcessorClass = Class.forName( "sun.awt.AWTAccessor" );
|
|
||||||
Class<?> compAccessorClass = Class.forName( "sun.awt.AWTAccessor$ComponentAccessor" );
|
|
||||||
AWTAccessor_getComponentAccessor = awtAcessorClass.getDeclaredMethod( "getComponentAccessor" );
|
|
||||||
AWTAccessor_ComponentAccessor_getPeer = compAccessorClass.getDeclaredMethod( "getPeer", Component.class );
|
|
||||||
|
|
||||||
Class<?> peerClass = Class.forName( "sun.awt.windows.WWindowPeer" );
|
|
||||||
WWindowPeer_setCustomDecorationTitleBarHeight = peerClass.getDeclaredMethod( "setCustomDecorationTitleBarHeight", int.class );
|
|
||||||
WWindowPeer_setCustomDecorationHitTestSpots = peerClass.getDeclaredMethod( "setCustomDecorationHitTestSpots", List.class );
|
|
||||||
WWindowPeer_setCustomDecorationTitleBarHeight.setAccessible( true );
|
|
||||||
WWindowPeer_setCustomDecorationHitTestSpots.setAccessible( true );
|
|
||||||
|
|
||||||
Window_hasCustomDecoration = Window.class.getDeclaredMethod( "hasCustomDecoration" );
|
|
||||||
Window_setHasCustomDecoration = Window.class.getDeclaredMethod( "setHasCustomDecoration" );
|
|
||||||
Window_hasCustomDecoration.setAccessible( true );
|
|
||||||
Window_setHasCustomDecoration.setAccessible( true );
|
|
||||||
|
|
||||||
supported = true;
|
|
||||||
} catch( Exception ex ) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//---- class JBRWindowTopBorder -------------------------------------------
|
|
||||||
|
|
||||||
static class JBRWindowTopBorder
|
|
||||||
extends BorderUIResource.EmptyBorderUIResource
|
|
||||||
{
|
|
||||||
private static JBRWindowTopBorder instance;
|
|
||||||
|
|
||||||
private final Color activeLightColor = new Color( 0x707070 );
|
|
||||||
private final Color activeDarkColor = new Color( 0x2D2E2F );
|
|
||||||
private final Color inactiveLightColor = new Color( 0xaaaaaa );
|
|
||||||
private final Color inactiveDarkColor = new Color( 0x494A4B );
|
|
||||||
|
|
||||||
private boolean colorizationAffectsBorders;
|
|
||||||
private Color activeColor;
|
|
||||||
|
|
||||||
static JBRWindowTopBorder getInstance() {
|
|
||||||
if( instance == null )
|
|
||||||
instance = new JBRWindowTopBorder();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
JBRWindowTopBorder() {
|
|
||||||
super( 1, 0, 0, 0 );
|
|
||||||
|
|
||||||
update();
|
|
||||||
installListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
void update() {
|
|
||||||
colorizationAffectsBorders = isColorizationColorAffectsBorders();
|
|
||||||
activeColor = calculateActiveBorderColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
void installListeners() {
|
|
||||||
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
|
||||||
toolkit.addPropertyChangeListener( "win.dwm.colorizationColor.affects.borders", e -> {
|
|
||||||
colorizationAffectsBorders = isColorizationColorAffectsBorders();
|
|
||||||
activeColor = calculateActiveBorderColor();
|
|
||||||
} );
|
|
||||||
|
|
||||||
PropertyChangeListener l = e -> {
|
|
||||||
activeColor = calculateActiveBorderColor();
|
|
||||||
};
|
|
||||||
toolkit.addPropertyChangeListener( "win.dwm.colorizationColor", l );
|
|
||||||
toolkit.addPropertyChangeListener( "win.dwm.colorizationColorBalance", l );
|
|
||||||
toolkit.addPropertyChangeListener( "win.frame.activeBorderColor", l );
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isColorizationColorAffectsBorders() {
|
|
||||||
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColor.affects.borders" );
|
|
||||||
return (value instanceof Boolean) ? (Boolean) value : true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Color getColorizationColor() {
|
|
||||||
return (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColor" );
|
|
||||||
}
|
|
||||||
|
|
||||||
int getColorizationColorBalance() {
|
|
||||||
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColorBalance" );
|
|
||||||
return (value instanceof Integer) ? (Integer) value : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Color calculateActiveBorderColor() {
|
|
||||||
if( !colorizationAffectsBorders )
|
|
||||||
return null;
|
|
||||||
|
|
||||||
Color colorizationColor = getColorizationColor();
|
|
||||||
if( colorizationColor != null ) {
|
|
||||||
int colorizationColorBalance = getColorizationColorBalance();
|
|
||||||
if( colorizationColorBalance < 0 || colorizationColorBalance > 100 )
|
|
||||||
colorizationColorBalance = 100;
|
|
||||||
|
|
||||||
if( colorizationColorBalance == 0 )
|
|
||||||
return new Color( 0xD9D9D9 );
|
|
||||||
if( colorizationColorBalance == 100 )
|
|
||||||
return colorizationColor;
|
|
||||||
|
|
||||||
float alpha = colorizationColorBalance / 100.0f;
|
|
||||||
float remainder = 1 - alpha;
|
|
||||||
int r = Math.round( colorizationColor.getRed() * alpha + 0xD9 * remainder );
|
|
||||||
int g = Math.round( colorizationColor.getGreen() * alpha + 0xD9 * remainder );
|
|
||||||
int b = Math.round( colorizationColor.getBlue() * alpha + 0xD9 * remainder );
|
|
||||||
|
|
||||||
// avoid potential IllegalArgumentException in Color constructor
|
|
||||||
r = Math.min( Math.max( r, 0 ), 255 );
|
|
||||||
g = Math.min( Math.max( g, 0 ), 255 );
|
|
||||||
b = Math.min( Math.max( b, 0 ), 255 );
|
|
||||||
|
|
||||||
return new Color( r, g, b );
|
|
||||||
}
|
|
||||||
|
|
||||||
Color activeBorderColor = (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.frame.activeBorderColor" );
|
|
||||||
return (activeBorderColor != null) ? activeBorderColor : UIManager.getColor( "MenuBar.borderColor" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
|
||||||
Window window = SwingUtilities.windowForComponent( c );
|
|
||||||
boolean active = window != null && window.isActive();
|
|
||||||
boolean dark = FlatLaf.isLafDark();
|
|
||||||
|
|
||||||
g.setColor( active
|
|
||||||
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
|
|
||||||
: (dark ? inactiveDarkColor : inactiveLightColor) );
|
|
||||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
|
||||||
g.fillRect( x, y, width, 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
void repaintBorder( Component c ) {
|
|
||||||
c.repaint( 0, 0, c.getWidth(), 1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.JTree;
|
||||||
|
import javax.swing.filechooser.FileSystemView;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.text.JTextComponent;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides Java version compatibility methods.
|
||||||
|
* <p>
|
||||||
|
* WARNING: This is private API and may change.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public class JavaCompatibility2
|
||||||
|
{
|
||||||
|
private static boolean getUIMethodInitialized;
|
||||||
|
private static MethodHandle getUIMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 8: getUI() method on various components (e.g. JButton, JList, etc)
|
||||||
|
* <br>
|
||||||
|
* Java 9: javax.swing.JComponent.getUI()
|
||||||
|
*/
|
||||||
|
public static ComponentUI getUI( JComponent c ) {
|
||||||
|
try {
|
||||||
|
// Java 9+
|
||||||
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
|
if( !getUIMethodInitialized ) {
|
||||||
|
getUIMethodInitialized = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
MethodType mt = MethodType.methodType( ComponentUI.class, new Class[0] );
|
||||||
|
getUIMethod = MethodHandles.publicLookup().findVirtual( JComponent.class, "getUI", mt );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
// ignore
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( getUIMethod != null )
|
||||||
|
return (ComponentUI) getUIMethod.invoke( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
// components often used (e.g. as view in scroll panes)
|
||||||
|
if( c instanceof JPanel )
|
||||||
|
return ((JPanel)c).getUI();
|
||||||
|
if( c instanceof JList )
|
||||||
|
return ((JList<?>)c).getUI();
|
||||||
|
if( c instanceof JTable )
|
||||||
|
return ((JTable)c).getUI();
|
||||||
|
if( c instanceof JTree )
|
||||||
|
return ((JTree)c).getUI();
|
||||||
|
if( c instanceof JTextComponent )
|
||||||
|
return ((JTextComponent)c).getUI();
|
||||||
|
|
||||||
|
// Java 8 and fallback
|
||||||
|
Method m = c.getClass().getMethod( "getUI" );
|
||||||
|
return (ComponentUI) m.invoke( c );
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
// ignore
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 8 - 11 on Windows: sun.awt.shell.ShellFolder.get( "fileChooserShortcutPanelFolders" )
|
||||||
|
* <br>
|
||||||
|
* Java 12: javax.swing.filechooser.FileSystemView.getChooserShortcutPanelFiles()
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
public static 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 8: sun.awt.shell.ShellFolder.get( "fileChooserComboBoxFolders" )
|
||||||
|
* <br>
|
||||||
|
* Java 9: javax.swing.filechooser.FileSystemView.getChooserComboBoxFiles()
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
public static File[] getChooserComboBoxFiles( FileSystemView fsv ) {
|
||||||
|
try {
|
||||||
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
|
Method m = fsv.getClass().getMethod( "getChooserComboBoxFiles" );
|
||||||
|
return (File[]) m.invoke( fsv );
|
||||||
|
} else {
|
||||||
|
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
|
||||||
|
Method m = cls.getMethod( "get", String.class );
|
||||||
|
return (File[]) m.invoke( null, "fileChooserComboBoxFolders" );
|
||||||
|
}
|
||||||
|
} 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -80,7 +80,7 @@ public class FontUtils
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a font family previously registered via {@link #registerFontFamilyLoader(String, Runnable)}.
|
* Loads a font family previously registered via {@link #registerFontFamilyLoader(String, Runnable)}.
|
||||||
* If the family is already loaded or no londer is registered for that family, nothing happens.
|
* If the family is already loaded or no loader is registered for that family, nothing happens.
|
||||||
*/
|
*/
|
||||||
public static void loadFontFamily( String family ) {
|
public static void loadFontFamily( String family ) {
|
||||||
if( !hasLoaders() )
|
if( !hasLoaders() )
|
||||||
@@ -109,7 +109,7 @@ public class FontUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all font familiy names available in the graphics environment.
|
* Returns all font family names available in the graphics environment.
|
||||||
* This invokes {@link GraphicsEnvironment#getAvailableFontFamilyNames()} and
|
* This invokes {@link GraphicsEnvironment#getAvailableFontFamilyNames()} and
|
||||||
* appends families registered for lazy loading via {@link #registerFontFamilyLoader(String, Runnable)}
|
* appends families registered for lazy loading via {@link #registerFontFamilyLoader(String, Runnable)}
|
||||||
* to the result.
|
* to the result.
|
||||||
|
|||||||
@@ -16,14 +16,17 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.util;
|
package com.formdev.flatlaf.util;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Rectangle;
|
||||||
import java.awt.font.GlyphVector;
|
import java.awt.font.GlyphVector;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.text.AttributedCharacterIterator;
|
import java.text.AttributedCharacterIterator;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.RepaintManager;
|
||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -192,7 +195,8 @@ public class HiDPIUtils
|
|||||||
|
|
||||||
case "Inter":
|
case "Inter":
|
||||||
case "Inter Light":
|
case "Inter Light":
|
||||||
case "Inter Semi Bold":
|
case "Inter Semi Bold": // Inter v3
|
||||||
|
case "Inter SemiBold": // Inter v4
|
||||||
case "Roboto":
|
case "Roboto":
|
||||||
case "Roboto Light":
|
case "Roboto Light":
|
||||||
case "Roboto Medium":
|
case "Roboto Medium":
|
||||||
@@ -321,4 +325,243 @@ public class HiDPIUtils
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repaints the given component.
|
||||||
|
* <p>
|
||||||
|
* See {@link #repaint(Component, int, int, int, int)} for more details.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static void repaint( Component c ) {
|
||||||
|
repaint( c, 0, 0, c.getWidth(), c.getHeight() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repaints the given component area.
|
||||||
|
* <p>
|
||||||
|
* See {@link #repaint(Component, int, int, int, int)} for more details.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static void repaint( Component c, Rectangle r ) {
|
||||||
|
repaint( c, r.x, r.y, r.width, r.height );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repaints the given component area.
|
||||||
|
* <p>
|
||||||
|
* Invokes {@link Component#repaint(int, int, int, int)} on the given component,
|
||||||
|
* <p>
|
||||||
|
* Use this method, instead of {@code Component.repaint(...)},
|
||||||
|
* to fix a problem in Swing when using scale factors that end on .25 or .75
|
||||||
|
* (e.g. 1.25, 1.75, 2.25, etc) and repainting single components, which may not
|
||||||
|
* repaint right and/or bottom 1px edge of component.
|
||||||
|
* <p>
|
||||||
|
* The problem may occur under following conditions:
|
||||||
|
* <ul>
|
||||||
|
* <li>using Java 9 or later
|
||||||
|
* <li>system scale factor is 125%, 175%, 225%, ...
|
||||||
|
* (Windows only; Java on macOS and Linux does not support fractional scale factors)
|
||||||
|
* <li>repaint whole component or right/bottom area of component
|
||||||
|
* <li>component is opaque; or component is contained in a opaque container
|
||||||
|
* that has same right/bottom bounds as component
|
||||||
|
* <li>component has bounds that Java/Swing scales different when repainting components
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static void repaint( Component c, int x, int y, int width, int height ) {
|
||||||
|
// repaint given component area
|
||||||
|
// Always invoke repaint() on given component, even if also invoked (below)
|
||||||
|
// on one of its ancestors, for the case that component overrides that method.
|
||||||
|
// Also RepaintManager "merges" the two repaints into one.
|
||||||
|
c.repaint( x, y, width, height );
|
||||||
|
|
||||||
|
if( RepaintManager.currentManager( c ) instanceof HiDPIRepaintManager )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// if necessary, also repaint given area in first ancestor that is larger than component
|
||||||
|
// to avoid clipping issue (see needsSpecialRepaint())
|
||||||
|
if( needsSpecialRepaint( c, x, y, width, height ) ) {
|
||||||
|
int x2 = x + c.getX();
|
||||||
|
int y2 = y + c.getY();
|
||||||
|
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
|
||||||
|
x2 += p.getX();
|
||||||
|
y2 += p.getY();
|
||||||
|
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() ) {
|
||||||
|
p.repaint( x2, y2, width, height );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There is a problem in Swing, when using scale factors that end on .25 or .75
|
||||||
|
* (e.g. 1.25, 1.75, 2.25, etc) and repainting single components, which may not
|
||||||
|
* repaint right and/or bottom 1px edge of component.
|
||||||
|
* <p>
|
||||||
|
* The component is first painted to an in-memory image,
|
||||||
|
* and then that image is copied to the screen.
|
||||||
|
* See {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}.
|
||||||
|
* <p>
|
||||||
|
* There are two clipping rectangles involved when copying the image to the screen:
|
||||||
|
* {@code sun.java2d.SunGraphics2D#devClip} and
|
||||||
|
* {@code sun.java2d.SunGraphics2D#usrClip}.
|
||||||
|
* <p>
|
||||||
|
* {@code devClip} is the device clipping in physical pixels.
|
||||||
|
* It gets the bounds of the painting component, which is either the passed component,
|
||||||
|
* or if it is non-opaque, then the first opaque ancestor of the passed component.
|
||||||
|
* It is calculated in {@code sun.java2d.SunGraphics2D#constrain()} while
|
||||||
|
* getting a graphics context via {@link JComponent#getGraphics()}.
|
||||||
|
* <p>
|
||||||
|
* {@code usrClip} is the user clipping, which is set via {@link Graphics} clipping methods.
|
||||||
|
* This is done in {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}.
|
||||||
|
* <p>
|
||||||
|
* The intersection of {@code devClip} and {@code usrClip}
|
||||||
|
* (computed in {@code sun.java2d.SunGraphics2D#validateCompClip()})
|
||||||
|
* is used to copy the image to the screen.
|
||||||
|
* <p>
|
||||||
|
* Unfortunately different scaling/rounding strategies are used to calculate
|
||||||
|
* the two clipping rectangles, which is the reason of the issue.
|
||||||
|
* <p>
|
||||||
|
* {@code devClip} (see {@code sun.java2d.SunGraphics2D#constrain()}):
|
||||||
|
* <pre>{@code
|
||||||
|
* int devX = (int) (x * scale);
|
||||||
|
* int devWidth = Math.round( width * scale )
|
||||||
|
* }</pre>
|
||||||
|
* {@code usrClip} (see {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}):
|
||||||
|
* <pre>{@code
|
||||||
|
* int usrX = (int) Math.ceil( (x * scale) - 0.5 );
|
||||||
|
* int usrWidth = ((int) Math.ceil( ((x + width) * scale) - 0.5 )) - usrX;
|
||||||
|
* }</pre>
|
||||||
|
* X/Y coordinates are always rounded down for {@code devClip}, but rounded up for {@code usrClip}.
|
||||||
|
* Width/height calculation is also different.
|
||||||
|
*/
|
||||||
|
private static boolean needsSpecialRepaint( Component c, int x, int y, int width, int height ) {
|
||||||
|
// no special repaint necessary for Java 8 or for macOS and Linux
|
||||||
|
// (Java on those platforms does not support fractional scale factors)
|
||||||
|
if( !SystemInfo.isJava_9_orLater || !SystemInfo.isWindows )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check whether repaint area is empty or no component given
|
||||||
|
// (same checks as in javax.swing.RepaintManager.addDirtyRegion0())
|
||||||
|
if( width <= 0 || height <= 0 || c == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check whether component has zero size
|
||||||
|
// (same checks as in javax.swing.RepaintManager.addDirtyRegion0())
|
||||||
|
int compWidth = c.getWidth();
|
||||||
|
int compHeight = c.getHeight();
|
||||||
|
if( compWidth <= 0 || compHeight <= 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check whether repaint area does span to right or bottom component edges
|
||||||
|
// (in this case, {@code devClip} is always larger than {@code usrClip})
|
||||||
|
if( x + width < compWidth && y + height < compHeight )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// if component is not opaque, Swing uses the first opaque ancestor for painting
|
||||||
|
if( !c.isOpaque() ) {
|
||||||
|
int x2 = x;
|
||||||
|
int y2 = y;
|
||||||
|
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
|
||||||
|
x2 += p.getX();
|
||||||
|
y2 += p.getY();
|
||||||
|
if( p.isOpaque() ) {
|
||||||
|
// check whether repaint area does span to right or bottom edges
|
||||||
|
// of the opaque ancestor component
|
||||||
|
// (in this case, {@code devClip} is always larger than {@code usrClip})
|
||||||
|
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() )
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether Special repaint is necessary for current scale factor
|
||||||
|
// (doing this check late because it temporary allocates some memory)
|
||||||
|
double scaleFactor = UIScale.getSystemScaleFactor( c.getGraphicsConfiguration() );
|
||||||
|
double fraction = scaleFactor - (int) scaleFactor;
|
||||||
|
if( fraction == 0 || fraction == 0.5 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs a {@link HiDPIRepaintManager} on Windows when running in Java 9+,
|
||||||
|
* but only if default repaint manager is currently installed.
|
||||||
|
* <p>
|
||||||
|
* Invoke once on application startup.
|
||||||
|
* Compatible with all/other LaFs.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static void installHiDPIRepaintManager() {
|
||||||
|
if( !SystemInfo.isJava_9_orLater || !SystemInfo.isWindows )
|
||||||
|
return;
|
||||||
|
|
||||||
|
RepaintManager manager = RepaintManager.currentManager( (Component) null );
|
||||||
|
if( manager.getClass() == RepaintManager.class )
|
||||||
|
RepaintManager.setCurrentManager( new HiDPIRepaintManager() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to {@link #repaint(Component, int, int, int, int)},
|
||||||
|
* but invokes callback instead of invoking {@link Component#repaint(int, int, int, int)}.
|
||||||
|
* <p>
|
||||||
|
* For use in custom repaint managers.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static void addDirtyRegion( JComponent c, int x, int y, int width, int height, DirtyRegionCallback callback ) {
|
||||||
|
if( needsSpecialRepaint( c, x, y, width, height ) ) {
|
||||||
|
int x2 = x + c.getX();
|
||||||
|
int y2 = y + c.getY();
|
||||||
|
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
|
||||||
|
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() && p instanceof JComponent ) {
|
||||||
|
callback.addDirtyRegion( (JComponent) p, x2, y2, width, height );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
x2 += p.getX();
|
||||||
|
y2 += p.getY();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.addDirtyRegion( c, x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface DirtyRegionCallback --------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For {@link HiDPIUtils#addDirtyRegion(JComponent, int, int, int, int, DirtyRegionCallback)}.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public interface DirtyRegionCallback {
|
||||||
|
void addDirtyRegion( JComponent c, int x, int y, int w, int h );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class HiDPIRepaintManager ------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A repaint manager that fixes a problem in Swing when repainting components
|
||||||
|
* at some scale factors (e.g. 125%, 175%, etc) on Windows.
|
||||||
|
* <p>
|
||||||
|
* Use {@link HiDPIUtils#installHiDPIRepaintManager()} to install it.
|
||||||
|
* <p>
|
||||||
|
* See {@link HiDPIUtils#repaint(Component, int, int, int, int)} for details.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static class HiDPIRepaintManager
|
||||||
|
extends RepaintManager
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void addDirtyRegion( JComponent c, int x, int y, int w, int h ) {
|
||||||
|
HiDPIUtils.addDirtyRegion( c, x, y, w, h, super::addDirtyRegion );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,11 @@ public class NativeLibrary
|
|||||||
try {
|
try {
|
||||||
// for development environment
|
// for development environment
|
||||||
if( "file".equals( libraryUrl.getProtocol() ) ) {
|
if( "file".equals( libraryUrl.getProtocol() ) ) {
|
||||||
File libraryFile = new File( libraryUrl.getPath() );
|
String binPath = libraryUrl.getPath();
|
||||||
|
String srcPath = binPath.replace( "flatlaf-core/bin/main/", "flatlaf-core/src/main/resources/" );
|
||||||
|
File libraryFile = new File( srcPath ); // use from 'src' folder if available
|
||||||
|
if( !libraryFile.isFile() )
|
||||||
|
libraryFile = new File( binPath ); // use from 'bin' or 'output' folder if available
|
||||||
if( libraryFile.isFile() ) {
|
if( libraryFile.isFile() ) {
|
||||||
// load library without copying
|
// load library without copying
|
||||||
System.load( libraryFile.getCanonicalPath() );
|
System.load( libraryFile.getCanonicalPath() );
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ public class UIScale
|
|||||||
if( SystemInfo.isWindows ) {
|
if( SystemInfo.isWindows ) {
|
||||||
// Special handling for Windows to be compatible with OS scaling,
|
// Special handling for Windows to be compatible with OS scaling,
|
||||||
// which distinguish between "screen scaling" and "text scaling".
|
// which distinguish between "screen scaling" and "text scaling".
|
||||||
// - Windows "screen scaling" scales everything (text, icon, gaps, etc)
|
// - Windows "screen scaling" scales everything (text, icon, gaps, etc.)
|
||||||
// and may have different scaling factors for each screen.
|
// and may have different scaling factors for each screen.
|
||||||
// - Windows "text scaling" increases only the font size, but on all screens.
|
// - Windows "text scaling" increases only the font size, but on all screens.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -246,6 +246,7 @@ PasswordField.revealIconColor = @foreground
|
|||||||
|
|
||||||
#---- Popup ----
|
#---- Popup ----
|
||||||
|
|
||||||
|
[mac]Popup.roundedBorderWidth = 1
|
||||||
Popup.dropShadowColor = #000
|
Popup.dropShadowColor = #000
|
||||||
Popup.dropShadowOpacity = 0.25
|
Popup.dropShadowOpacity = 0.25
|
||||||
|
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ ComboBox.popupInsets = 0,0,0,0
|
|||||||
ComboBox.selectionInsets = 0,0,0,0
|
ComboBox.selectionInsets = 0,0,0,0
|
||||||
ComboBox.selectionArc = 0
|
ComboBox.selectionArc = 0
|
||||||
ComboBox.borderCornerRadius = $Popup.borderCornerRadius
|
ComboBox.borderCornerRadius = $Popup.borderCornerRadius
|
||||||
|
[mac]ComboBox.roundedBorderWidth = $Popup.roundedBorderWidth
|
||||||
|
|
||||||
|
|
||||||
#---- Component ----
|
#---- Component ----
|
||||||
@@ -505,6 +506,7 @@ PasswordField.revealIcon = com.formdev.flatlaf.icons.FlatRevealIcon
|
|||||||
#---- Popup ----
|
#---- Popup ----
|
||||||
|
|
||||||
Popup.borderCornerRadius = 4
|
Popup.borderCornerRadius = 4
|
||||||
|
[mac]Popup.roundedBorderWidth = 0
|
||||||
Popup.dropShadowPainted = true
|
Popup.dropShadowPainted = true
|
||||||
Popup.dropShadowInsets = -4,-4,4,4
|
Popup.dropShadowInsets = -4,-4,4,4
|
||||||
|
|
||||||
@@ -514,6 +516,7 @@ Popup.dropShadowInsets = -4,-4,4,4
|
|||||||
PopupMenu.border = com.formdev.flatlaf.ui.FlatPopupMenuBorder
|
PopupMenu.border = com.formdev.flatlaf.ui.FlatPopupMenuBorder
|
||||||
PopupMenu.borderInsets = 4,1,4,1
|
PopupMenu.borderInsets = 4,1,4,1
|
||||||
PopupMenu.borderCornerRadius = $Popup.borderCornerRadius
|
PopupMenu.borderCornerRadius = $Popup.borderCornerRadius
|
||||||
|
[mac]PopupMenu.roundedBorderWidth = $Popup.roundedBorderWidth
|
||||||
PopupMenu.background = @menuBackground
|
PopupMenu.background = @menuBackground
|
||||||
PopupMenu.scrollArrowColor = @buttonArrowColor
|
PopupMenu.scrollArrowColor = @buttonArrowColor
|
||||||
|
|
||||||
@@ -597,10 +600,15 @@ ScrollBar.allowsAbsolutePositioning = true
|
|||||||
|
|
||||||
#---- ScrollPane ----
|
#---- ScrollPane ----
|
||||||
|
|
||||||
ScrollPane.border = com.formdev.flatlaf.ui.FlatBorder
|
ScrollPane.border = com.formdev.flatlaf.ui.FlatScrollPaneBorder
|
||||||
ScrollPane.background = $ScrollBar.track
|
ScrollPane.background = $ScrollBar.track
|
||||||
ScrollPane.fillUpperCorner = true
|
ScrollPane.fillUpperCorner = true
|
||||||
ScrollPane.smoothScrolling = true
|
ScrollPane.smoothScrolling = true
|
||||||
|
ScrollPane.arc = 0
|
||||||
|
#ScrollPane.List.arc = -1
|
||||||
|
#ScrollPane.Table.arc = -1
|
||||||
|
#ScrollPane.TextComponent.arc = -1
|
||||||
|
#ScrollPane.Tree.arc = -1
|
||||||
|
|
||||||
|
|
||||||
#---- SearchField ----
|
#---- SearchField ----
|
||||||
@@ -697,6 +705,8 @@ TabbedPane.tabAreaAlignment = leading
|
|||||||
TabbedPane.tabAlignment = center
|
TabbedPane.tabAlignment = center
|
||||||
# allowed values: preferred, equal or compact
|
# allowed values: preferred, equal or compact
|
||||||
TabbedPane.tabWidthMode = preferred
|
TabbedPane.tabWidthMode = preferred
|
||||||
|
# allowed values: none, auto, left or right
|
||||||
|
TabbedPane.tabRotation = none
|
||||||
|
|
||||||
# allowed values: underlined or card
|
# allowed values: underlined or card
|
||||||
TabbedPane.tabType = underlined
|
TabbedPane.tabType = underlined
|
||||||
@@ -729,9 +739,11 @@ Table.rowHeight = 20
|
|||||||
Table.showHorizontalLines = false
|
Table.showHorizontalLines = false
|
||||||
Table.showVerticalLines = false
|
Table.showVerticalLines = false
|
||||||
Table.showTrailingVerticalLine = false
|
Table.showTrailingVerticalLine = false
|
||||||
|
Table.paintOutsideAlternateRows = false
|
||||||
|
Table.editorSelectAllOnStartEditing = true
|
||||||
Table.consistentHomeEndKeyBehavior = true
|
Table.consistentHomeEndKeyBehavior = true
|
||||||
Table.intercellSpacing = 0,0
|
Table.intercellSpacing = 0,0
|
||||||
Table.scrollPaneBorder = com.formdev.flatlaf.ui.FlatBorder
|
Table.scrollPaneBorder = com.formdev.flatlaf.ui.FlatScrollPaneBorder
|
||||||
Table.ascendingSortIcon = com.formdev.flatlaf.icons.FlatAscendingSortIcon
|
Table.ascendingSortIcon = com.formdev.flatlaf.icons.FlatAscendingSortIcon
|
||||||
Table.descendingSortIcon = com.formdev.flatlaf.icons.FlatDescendingSortIcon
|
Table.descendingSortIcon = com.formdev.flatlaf.icons.FlatDescendingSortIcon
|
||||||
Table.sortIconColor = @icon
|
Table.sortIconColor = @icon
|
||||||
@@ -819,7 +831,6 @@ TitlePane.centerTitleIfMenuBarEmbedded = true
|
|||||||
TitlePane.showIconBesideTitle = false
|
TitlePane.showIconBesideTitle = false
|
||||||
TitlePane.menuBarTitleGap = 40
|
TitlePane.menuBarTitleGap = 40
|
||||||
TitlePane.menuBarTitleMinimumGap = 12
|
TitlePane.menuBarTitleMinimumGap = 12
|
||||||
TitlePane.menuBarResizeHeight = 4
|
|
||||||
TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon
|
TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon
|
||||||
TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon
|
TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon
|
||||||
TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon
|
TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon
|
||||||
@@ -902,6 +913,7 @@ ToolTipManager.enableToolTipMode = activeApplication
|
|||||||
#---- ToolTip ----
|
#---- ToolTip ----
|
||||||
|
|
||||||
ToolTip.borderCornerRadius = $Popup.borderCornerRadius
|
ToolTip.borderCornerRadius = $Popup.borderCornerRadius
|
||||||
|
[mac]ToolTip.roundedBorderWidth = $Popup.roundedBorderWidth
|
||||||
|
|
||||||
|
|
||||||
#---- Tree ----
|
#---- Tree ----
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -295,3 +295,8 @@ ToggleButton.disabledBackground = $Button.disabledBackground
|
|||||||
ToggleButton.selectedBackground = lighten($ToggleButton.background,20%,derived)
|
ToggleButton.selectedBackground = lighten($ToggleButton.background,20%,derived)
|
||||||
|
|
||||||
ToggleButton.toolbar.selectedBackground = #fff3
|
ToggleButton.toolbar.selectedBackground = #fff3
|
||||||
|
|
||||||
|
|
||||||
|
#---- ToolBar ----
|
||||||
|
|
||||||
|
ToolBar.hoverButtonGroupArc = 14
|
||||||
|
|||||||
@@ -291,3 +291,8 @@ TextPane.selectionForeground = @textSelectionForeground
|
|||||||
#---- ToggleButton ----
|
#---- ToggleButton ----
|
||||||
|
|
||||||
ToggleButton.disabledBackground = $Button.disabledBackground
|
ToggleButton.disabledBackground = $Button.disabledBackground
|
||||||
|
|
||||||
|
|
||||||
|
#---- ToolBar ----
|
||||||
|
|
||||||
|
ToolBar.hoverButtonGroupArc = 14
|
||||||
|
|||||||
@@ -84,10 +84,12 @@ public class TestUIDefaultsLoader
|
|||||||
void parseBorders() {
|
void parseBorders() {
|
||||||
Insets insets = new Insets( 1,2,3,4 );
|
Insets insets = new Insets( 1,2,3,4 );
|
||||||
assertBorderEquals( new FlatEmptyBorder( insets ), "1,2,3,4" );
|
assertBorderEquals( new FlatEmptyBorder( insets ), "1,2,3,4" );
|
||||||
|
assertBorderEquals( new FlatEmptyBorder( insets ), "1,2,3,4,,," );
|
||||||
assertBorderEquals( new FlatLineBorder( insets, Color.red ), "1,2,3,4,#f00" );
|
assertBorderEquals( new FlatLineBorder( insets, Color.red ), "1,2,3,4,#f00" );
|
||||||
assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, 0 ), "1,2,3,4,#f00,2.5" );
|
assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, -1 ), "1,2,3,4,#f00,2.5" );
|
||||||
assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, 6 ), "1,2,3,4,#f00,2.5,6" );
|
assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, 6 ), "1,2,3,4,#f00,2.5,6" );
|
||||||
assertBorderEquals( new FlatLineBorder( insets, Color.red, 1, 6 ), "1,2,3,4,#f00,,6" );
|
assertBorderEquals( new FlatLineBorder( insets, Color.red, 1, 6 ), "1,2,3,4,#f00,,6" );
|
||||||
|
assertBorderEquals( new FlatLineBorder( insets, null, 1, 6 ), "1,2,3,4,,,6" );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertBorderEquals( Border expected, String actualStyle ) {
|
private void assertBorderEquals( Border expected, String actualStyle ) {
|
||||||
|
|||||||
@@ -253,7 +253,8 @@ public class TestFlatStyleableInfo
|
|||||||
FlatLabelUI ui = (FlatLabelUI) c.getUI();
|
FlatLabelUI ui = (FlatLabelUI) c.getUI();
|
||||||
|
|
||||||
Map<String, Class<?>> expected = expectedMap(
|
Map<String, Class<?>> expected = expectedMap(
|
||||||
"disabledForeground", Color.class
|
"disabledForeground", Color.class,
|
||||||
|
"arc", int.class
|
||||||
);
|
);
|
||||||
|
|
||||||
assertMapEquals( expected, ui.getStyleableInfos( c ) );
|
assertMapEquals( expected, ui.getStyleableInfos( c ) );
|
||||||
@@ -601,7 +602,7 @@ public class TestFlatStyleableInfo
|
|||||||
);
|
);
|
||||||
|
|
||||||
// border
|
// border
|
||||||
flatBorder( expected );
|
flatScrollPaneBorder( expected );
|
||||||
|
|
||||||
assertMapEquals( expected, ui.getStyleableInfos( c ) );
|
assertMapEquals( expected, ui.getStyleableInfos( c ) );
|
||||||
}
|
}
|
||||||
@@ -689,6 +690,9 @@ public class TestFlatStyleableInfo
|
|||||||
|
|
||||||
Map<String, Class<?>> expected = expectedMap(
|
Map<String, Class<?>> expected = expectedMap(
|
||||||
"arrowType", String.class,
|
"arrowType", String.class,
|
||||||
|
"draggingColor", Color.class,
|
||||||
|
"hoverColor", Color.class,
|
||||||
|
"pressedColor", Color.class,
|
||||||
"oneTouchArrowColor", Color.class,
|
"oneTouchArrowColor", Color.class,
|
||||||
"oneTouchHoverArrowColor", Color.class,
|
"oneTouchHoverArrowColor", Color.class,
|
||||||
"oneTouchPressedArrowColor", Color.class,
|
"oneTouchPressedArrowColor", Color.class,
|
||||||
@@ -752,6 +756,7 @@ public class TestFlatStyleableInfo
|
|||||||
"tabAreaAlignment", String.class,
|
"tabAreaAlignment", String.class,
|
||||||
"tabAlignment", String.class,
|
"tabAlignment", String.class,
|
||||||
"tabWidthMode", String.class,
|
"tabWidthMode", String.class,
|
||||||
|
"tabRotation", String.class,
|
||||||
|
|
||||||
"arrowType", String.class,
|
"arrowType", String.class,
|
||||||
"buttonInsets", Insets.class,
|
"buttonInsets", Insets.class,
|
||||||
@@ -795,6 +800,8 @@ public class TestFlatStyleableInfo
|
|||||||
"selectionForeground", Color.class,
|
"selectionForeground", Color.class,
|
||||||
"selectionInactiveBackground", Color.class,
|
"selectionInactiveBackground", Color.class,
|
||||||
"selectionInactiveForeground", Color.class,
|
"selectionInactiveForeground", Color.class,
|
||||||
|
"selectionInsets", Insets.class,
|
||||||
|
"selectionArc", int.class,
|
||||||
|
|
||||||
// FlatTableCellBorder
|
// FlatTableCellBorder
|
||||||
"cellMargins", Insets.class,
|
"cellMargins", Insets.class,
|
||||||
@@ -925,7 +932,10 @@ public class TestFlatStyleableInfo
|
|||||||
"hoverButtonGroupBackground", Color.class,
|
"hoverButtonGroupBackground", Color.class,
|
||||||
|
|
||||||
"borderMargins", Insets.class,
|
"borderMargins", Insets.class,
|
||||||
"gripColor", Color.class
|
"gripColor", Color.class,
|
||||||
|
|
||||||
|
"separatorWidth", Integer.class,
|
||||||
|
"separatorColor", Color.class
|
||||||
);
|
);
|
||||||
|
|
||||||
assertMapEquals( expected, ui.getStyleableInfos( c ) );
|
assertMapEquals( expected, ui.getStyleableInfos( c ) );
|
||||||
@@ -986,12 +996,20 @@ public class TestFlatStyleableInfo
|
|||||||
"disabledBorderColor", Color.class,
|
"disabledBorderColor", Color.class,
|
||||||
"focusedBorderColor", Color.class,
|
"focusedBorderColor", Color.class,
|
||||||
"hoverBorderColor", Color.class,
|
"hoverBorderColor", Color.class,
|
||||||
|
"pressedBorderColor", Color.class,
|
||||||
|
|
||||||
|
"selectedBorderColor", Color.class,
|
||||||
|
"disabledSelectedBorderColor", Color.class,
|
||||||
|
"focusedSelectedBorderColor", Color.class,
|
||||||
|
"hoverSelectedBorderColor", Color.class,
|
||||||
|
"pressedSelectedBorderColor", Color.class,
|
||||||
|
|
||||||
"default.borderWidth", float.class,
|
"default.borderWidth", float.class,
|
||||||
"default.borderColor", Color.class,
|
"default.borderColor", Color.class,
|
||||||
"default.focusedBorderColor", Color.class,
|
"default.focusedBorderColor", Color.class,
|
||||||
"default.focusColor", Color.class,
|
"default.focusColor", Color.class,
|
||||||
"default.hoverBorderColor", Color.class,
|
"default.hoverBorderColor", Color.class,
|
||||||
|
"default.pressedBorderColor", Color.class,
|
||||||
|
|
||||||
"toolbar.focusWidth", float.class,
|
"toolbar.focusWidth", float.class,
|
||||||
"toolbar.focusColor", Color.class,
|
"toolbar.focusColor", Color.class,
|
||||||
@@ -1005,17 +1023,23 @@ public class TestFlatStyleableInfo
|
|||||||
|
|
||||||
expectedMap( expected,
|
expectedMap( expected,
|
||||||
"arc", int.class,
|
"arc", int.class,
|
||||||
|
|
||||||
"roundRect", Boolean.class
|
"roundRect", Boolean.class
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void flatScrollPaneBorder( Map<String, Class<?>> expected ) {
|
||||||
|
flatBorder( expected );
|
||||||
|
|
||||||
|
expectedMap( expected,
|
||||||
|
"arc", int.class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private void flatTextBorder( Map<String, Class<?>> expected ) {
|
private void flatTextBorder( Map<String, Class<?>> expected ) {
|
||||||
flatBorder( expected );
|
flatBorder( expected );
|
||||||
|
|
||||||
expectedMap( expected,
|
expectedMap( expected,
|
||||||
"arc", int.class,
|
"arc", int.class,
|
||||||
|
|
||||||
"roundRect", Boolean.class
|
"roundRect", Boolean.class
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -305,6 +305,9 @@ public class TestFlatStyleableValue
|
|||||||
testColor( c, ui, "buttonPressedArrowColor", 0x123456 );
|
testColor( c, ui, "buttonPressedArrowColor", 0x123456 );
|
||||||
|
|
||||||
testColor( c, ui, "popupBackground", 0x123456 );
|
testColor( c, ui, "popupBackground", 0x123456 );
|
||||||
|
testInsets( c, ui, "popupInsets", 1,2,3,4 );
|
||||||
|
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
|
||||||
|
testInteger( c, ui, "selectionArc", 123 );
|
||||||
|
|
||||||
// border
|
// border
|
||||||
flatRoundBorder( c, ui );
|
flatRoundBorder( c, ui );
|
||||||
@@ -355,6 +358,7 @@ public class TestFlatStyleableValue
|
|||||||
FlatLabelUI ui = (FlatLabelUI) c.getUI();
|
FlatLabelUI ui = (FlatLabelUI) c.getUI();
|
||||||
|
|
||||||
testColor( c, ui, "disabledForeground", 0x123456 );
|
testColor( c, ui, "disabledForeground", 0x123456 );
|
||||||
|
testInteger( c, ui, "arc", 123 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -366,6 +370,8 @@ public class TestFlatStyleableValue
|
|||||||
testColor( c, ui, "selectionForeground", 0x123456 );
|
testColor( c, ui, "selectionForeground", 0x123456 );
|
||||||
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
|
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
|
||||||
testColor( c, ui, "selectionInactiveForeground", 0x123456 );
|
testColor( c, ui, "selectionInactiveForeground", 0x123456 );
|
||||||
|
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
|
||||||
|
testInteger( c, ui, "selectionArc", 123 );
|
||||||
|
|
||||||
// FlatListCellBorder
|
// FlatListCellBorder
|
||||||
testInsets( c, ui, "cellMargins", 1,2,3,4 );
|
testInsets( c, ui, "cellMargins", 1,2,3,4 );
|
||||||
@@ -625,7 +631,7 @@ public class TestFlatStyleableValue
|
|||||||
FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI();
|
FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI();
|
||||||
|
|
||||||
// border
|
// border
|
||||||
flatBorder( c, ui );
|
flatScrollPaneBorder( c, ui );
|
||||||
|
|
||||||
testBoolean( c, ui, "showButtons", true );
|
testBoolean( c, ui, "showButtons", true );
|
||||||
}
|
}
|
||||||
@@ -699,6 +705,9 @@ public class TestFlatStyleableValue
|
|||||||
FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI();
|
FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI();
|
||||||
|
|
||||||
testString( c, ui, "arrowType", "chevron" );
|
testString( c, ui, "arrowType", "chevron" );
|
||||||
|
testColor( c, ui, "draggingColor", 0x123456 );
|
||||||
|
testColor( c, ui, "hoverColor", 0x123456 );
|
||||||
|
testColor( c, ui, "pressedColor", 0x123456 );
|
||||||
testColor( c, ui, "oneTouchArrowColor", 0x123456 );
|
testColor( c, ui, "oneTouchArrowColor", 0x123456 );
|
||||||
testColor( c, ui, "oneTouchHoverArrowColor", 0x123456 );
|
testColor( c, ui, "oneTouchHoverArrowColor", 0x123456 );
|
||||||
testColor( c, ui, "oneTouchPressedArrowColor", 0x123456 );
|
testColor( c, ui, "oneTouchPressedArrowColor", 0x123456 );
|
||||||
@@ -758,6 +767,7 @@ public class TestFlatStyleableValue
|
|||||||
testString( c, ui, "tabAreaAlignment", "leading" );
|
testString( c, ui, "tabAreaAlignment", "leading" );
|
||||||
testString( c, ui, "tabAlignment", "center" );
|
testString( c, ui, "tabAlignment", "center" );
|
||||||
testString( c, ui, "tabWidthMode", "preferred" );
|
testString( c, ui, "tabWidthMode", "preferred" );
|
||||||
|
testString( c, ui, "tabRotation", "none" );
|
||||||
|
|
||||||
testString( c, ui, "arrowType", "chevron" );
|
testString( c, ui, "arrowType", "chevron" );
|
||||||
testInsets( c, ui, "buttonInsets", 1,2,3,4 );
|
testInsets( c, ui, "buttonInsets", 1,2,3,4 );
|
||||||
@@ -797,6 +807,8 @@ public class TestFlatStyleableValue
|
|||||||
testColor( c, ui, "selectionForeground", 0x123456 );
|
testColor( c, ui, "selectionForeground", 0x123456 );
|
||||||
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
|
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
|
||||||
testColor( c, ui, "selectionInactiveForeground", 0x901324 );
|
testColor( c, ui, "selectionInactiveForeground", 0x901324 );
|
||||||
|
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
|
||||||
|
testInteger( c, ui, "selectionArc", 123 );
|
||||||
|
|
||||||
// FlatTableCellBorder
|
// FlatTableCellBorder
|
||||||
testInsets( c, ui, "cellMargins", 1,2,3,4 );
|
testInsets( c, ui, "cellMargins", 1,2,3,4 );
|
||||||
@@ -902,6 +914,9 @@ public class TestFlatStyleableValue
|
|||||||
|
|
||||||
testInsets( c, ui, "borderMargins", 1,2,3,4 );
|
testInsets( c, ui, "borderMargins", 1,2,3,4 );
|
||||||
testColor( c, ui, "gripColor", 0x123456 );
|
testColor( c, ui, "gripColor", 0x123456 );
|
||||||
|
|
||||||
|
testInteger( c, ui, "separatorWidth", 123 );
|
||||||
|
testColor( c, ui, "separatorColor", 0x123456 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -923,6 +938,8 @@ public class TestFlatStyleableValue
|
|||||||
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
|
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
|
||||||
testColor( c, ui, "selectionInactiveForeground", 0x123456 );
|
testColor( c, ui, "selectionInactiveForeground", 0x123456 );
|
||||||
testColor( c, ui, "selectionBorderColor", 0x123456 );
|
testColor( c, ui, "selectionBorderColor", 0x123456 );
|
||||||
|
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
|
||||||
|
testInteger( c, ui, "selectionArc", 123 );
|
||||||
testBoolean( c, ui, "wideSelection", true );
|
testBoolean( c, ui, "wideSelection", true );
|
||||||
testBoolean( c, ui, "showCellFocusIndicator", true );
|
testBoolean( c, ui, "showCellFocusIndicator", true );
|
||||||
|
|
||||||
@@ -948,12 +965,20 @@ public class TestFlatStyleableValue
|
|||||||
testColor( c, ui, "disabledBorderColor", 0x123456 );
|
testColor( c, ui, "disabledBorderColor", 0x123456 );
|
||||||
testColor( c, ui, "focusedBorderColor", 0x123456 );
|
testColor( c, ui, "focusedBorderColor", 0x123456 );
|
||||||
testColor( c, ui, "hoverBorderColor", 0x123456 );
|
testColor( c, ui, "hoverBorderColor", 0x123456 );
|
||||||
|
testColor( c, ui, "pressedBorderColor", 0x123456 );
|
||||||
|
|
||||||
|
testColor( c, ui, "selectedBorderColor", 0x123456 );
|
||||||
|
testColor( c, ui, "disabledSelectedBorderColor", 0x123456 );
|
||||||
|
testColor( c, ui, "focusedSelectedBorderColor", 0x123456 );
|
||||||
|
testColor( c, ui, "hoverSelectedBorderColor", 0x123456 );
|
||||||
|
testColor( c, ui, "pressedSelectedBorderColor", 0x123456 );
|
||||||
|
|
||||||
testFloat( c, ui, "default.borderWidth", 1.23f );
|
testFloat( c, ui, "default.borderWidth", 1.23f );
|
||||||
testColor( c, ui, "default.borderColor", 0x123456 );
|
testColor( c, ui, "default.borderColor", 0x123456 );
|
||||||
testColor( c, ui, "default.focusedBorderColor", 0x123456 );
|
testColor( c, ui, "default.focusedBorderColor", 0x123456 );
|
||||||
testColor( c, ui, "default.focusColor", 0x123456 );
|
testColor( c, ui, "default.focusColor", 0x123456 );
|
||||||
testColor( c, ui, "default.hoverBorderColor", 0x123456 );
|
testColor( c, ui, "default.hoverBorderColor", 0x123456 );
|
||||||
|
testColor( c, ui, "default.pressedBorderColor", 0x123456 );
|
||||||
|
|
||||||
testFloat( c, ui, "toolbar.focusWidth", 1.23f );
|
testFloat( c, ui, "toolbar.focusWidth", 1.23f );
|
||||||
testColor( c, ui, "toolbar.focusColor", 0x123456 );
|
testColor( c, ui, "toolbar.focusColor", 0x123456 );
|
||||||
@@ -965,15 +990,19 @@ public class TestFlatStyleableValue
|
|||||||
flatBorder( c, ui );
|
flatBorder( c, ui );
|
||||||
|
|
||||||
testInteger( c, ui, "arc", 123 );
|
testInteger( c, ui, "arc", 123 );
|
||||||
|
|
||||||
testBoolean( c, ui, "roundRect", true );
|
testBoolean( c, ui, "roundRect", true );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void flatScrollPaneBorder( JComponent c, StyleableUI ui ) {
|
||||||
|
flatBorder( c, ui );
|
||||||
|
|
||||||
|
testInteger( c, ui, "arc", 123 );
|
||||||
|
}
|
||||||
|
|
||||||
private void flatTextBorder( JComponent c, StyleableUI ui ) {
|
private void flatTextBorder( JComponent c, StyleableUI ui ) {
|
||||||
flatBorder( c, ui );
|
flatBorder( c, ui );
|
||||||
|
|
||||||
testInteger( c, ui, "arc", 123 );
|
testInteger( c, ui, "arc", 123 );
|
||||||
|
|
||||||
testBoolean( c, ui, "roundRect", true );
|
testBoolean( c, ui, "roundRect", true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1014,12 +1043,20 @@ public class TestFlatStyleableValue
|
|||||||
testValue( border, "disabledBorderColor", Color.WHITE );
|
testValue( border, "disabledBorderColor", Color.WHITE );
|
||||||
testValue( border, "focusedBorderColor", Color.WHITE );
|
testValue( border, "focusedBorderColor", Color.WHITE );
|
||||||
testValue( border, "hoverBorderColor", Color.WHITE );
|
testValue( border, "hoverBorderColor", Color.WHITE );
|
||||||
|
testValue( border, "pressedBorderColor", Color.WHITE );
|
||||||
|
|
||||||
|
testValue( border, "selectedBorderColor", Color.WHITE );
|
||||||
|
testValue( border, "disabledSelectedBorderColor", Color.WHITE );
|
||||||
|
testValue( border, "focusedSelectedBorderColor", Color.WHITE );
|
||||||
|
testValue( border, "hoverSelectedBorderColor", Color.WHITE );
|
||||||
|
testValue( border, "pressedSelectedBorderColor", Color.WHITE );
|
||||||
|
|
||||||
testValue( border, "default.borderWidth", 2f );
|
testValue( border, "default.borderWidth", 2f );
|
||||||
testValue( border, "default.borderColor", Color.WHITE );
|
testValue( border, "default.borderColor", Color.WHITE );
|
||||||
testValue( border, "default.focusedBorderColor", Color.WHITE );
|
testValue( border, "default.focusedBorderColor", Color.WHITE );
|
||||||
testValue( border, "default.focusColor", Color.WHITE );
|
testValue( border, "default.focusColor", Color.WHITE );
|
||||||
testValue( border, "default.hoverBorderColor", Color.WHITE );
|
testValue( border, "default.hoverBorderColor", Color.WHITE );
|
||||||
|
testValue( border, "default.pressedBorderColor", Color.WHITE );
|
||||||
|
|
||||||
testValue( border, "toolbar.focusWidth", 1.5f );
|
testValue( border, "toolbar.focusWidth", 1.5f );
|
||||||
testValue( border, "toolbar.focusColor", Color.WHITE );
|
testValue( border, "toolbar.focusColor", Color.WHITE );
|
||||||
@@ -1034,6 +1071,17 @@ public class TestFlatStyleableValue
|
|||||||
// FlatRoundBorder extends FlatBorder
|
// FlatRoundBorder extends FlatBorder
|
||||||
flatBorder( border );
|
flatBorder( border );
|
||||||
|
|
||||||
|
testValue( border, "arc", 6 );
|
||||||
|
testValue( border, "roundRect", true );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void flatScrollPaneBorder() {
|
||||||
|
FlatScrollPaneBorder border = new FlatScrollPaneBorder();
|
||||||
|
|
||||||
|
// FlatScrollPaneBorder extends FlatBorder
|
||||||
|
flatBorder( border );
|
||||||
|
|
||||||
testValue( border, "arc", 6 );
|
testValue( border, "arc", 6 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1045,6 +1093,7 @@ public class TestFlatStyleableValue
|
|||||||
flatBorder( border );
|
flatBorder( border );
|
||||||
|
|
||||||
testValue( border, "arc", 6 );
|
testValue( border, "arc", 6 );
|
||||||
|
testValue( border, "roundRect", true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -412,6 +412,7 @@ public class TestFlatStyling
|
|||||||
FlatLabelUI ui = (FlatLabelUI) c.getUI();
|
FlatLabelUI ui = (FlatLabelUI) c.getUI();
|
||||||
|
|
||||||
ui.applyStyle( c, "disabledForeground: #fff" );
|
ui.applyStyle( c, "disabledForeground: #fff" );
|
||||||
|
ui.applyStyle( c, "arc: 8" );
|
||||||
|
|
||||||
// JComponent properties
|
// JComponent properties
|
||||||
ui.applyStyle( c, "background: #fff" );
|
ui.applyStyle( c, "background: #fff" );
|
||||||
@@ -760,7 +761,7 @@ public class TestFlatStyling
|
|||||||
FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI();
|
FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI();
|
||||||
|
|
||||||
// border
|
// border
|
||||||
flatBorder( style -> ui.applyStyle( style ) );
|
flatScrollPaneBorder( style -> ui.applyStyle( style ) );
|
||||||
|
|
||||||
ui.applyStyle( "showButtons: true" );
|
ui.applyStyle( "showButtons: true" );
|
||||||
|
|
||||||
@@ -870,6 +871,9 @@ public class TestFlatStyling
|
|||||||
FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI();
|
FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI();
|
||||||
|
|
||||||
ui.applyStyle( "arrowType: chevron" );
|
ui.applyStyle( "arrowType: chevron" );
|
||||||
|
ui.applyStyle( "draggingColor: #fff" );
|
||||||
|
ui.applyStyle( "hoverColor: #fff" );
|
||||||
|
ui.applyStyle( "pressedColor: #fff" );
|
||||||
ui.applyStyle( "oneTouchArrowColor: #fff" );
|
ui.applyStyle( "oneTouchArrowColor: #fff" );
|
||||||
ui.applyStyle( "oneTouchHoverArrowColor: #fff" );
|
ui.applyStyle( "oneTouchHoverArrowColor: #fff" );
|
||||||
ui.applyStyle( "oneTouchPressedArrowColor: #fff" );
|
ui.applyStyle( "oneTouchPressedArrowColor: #fff" );
|
||||||
@@ -937,6 +941,7 @@ public class TestFlatStyling
|
|||||||
ui.applyStyle( "tabAreaAlignment: leading" );
|
ui.applyStyle( "tabAreaAlignment: leading" );
|
||||||
ui.applyStyle( "tabAlignment: center" );
|
ui.applyStyle( "tabAlignment: center" );
|
||||||
ui.applyStyle( "tabWidthMode: preferred" );
|
ui.applyStyle( "tabWidthMode: preferred" );
|
||||||
|
ui.applyStyle( "tabRotation: none" );
|
||||||
|
|
||||||
ui.applyStyle( "arrowType: chevron" );
|
ui.applyStyle( "arrowType: chevron" );
|
||||||
ui.applyStyle( "buttonInsets: 1,2,3,4" );
|
ui.applyStyle( "buttonInsets: 1,2,3,4" );
|
||||||
@@ -982,6 +987,8 @@ public class TestFlatStyling
|
|||||||
ui.applyStyle( "selectionForeground: #fff" );
|
ui.applyStyle( "selectionForeground: #fff" );
|
||||||
ui.applyStyle( "selectionInactiveBackground: #fff" );
|
ui.applyStyle( "selectionInactiveBackground: #fff" );
|
||||||
ui.applyStyle( "selectionInactiveForeground: #fff" );
|
ui.applyStyle( "selectionInactiveForeground: #fff" );
|
||||||
|
ui.applyStyle( "selectionInsets: 1,2,3,4" );
|
||||||
|
ui.applyStyle( "selectionArc: 8" );
|
||||||
|
|
||||||
// FlatTableCellBorder
|
// FlatTableCellBorder
|
||||||
ui.applyStyle( "cellMargins: 1,2,3,4" );
|
ui.applyStyle( "cellMargins: 1,2,3,4" );
|
||||||
@@ -1146,6 +1153,9 @@ public class TestFlatStyling
|
|||||||
ui.applyStyle( "borderMargins: 1,2,3,4" );
|
ui.applyStyle( "borderMargins: 1,2,3,4" );
|
||||||
ui.applyStyle( "gripColor: #fff" );
|
ui.applyStyle( "gripColor: #fff" );
|
||||||
|
|
||||||
|
ui.applyStyle( "separatorWidth: 6" );
|
||||||
|
ui.applyStyle( "separatorColor: #fff" );
|
||||||
|
|
||||||
// JComponent properties
|
// JComponent properties
|
||||||
ui.applyStyle( "background: #fff" );
|
ui.applyStyle( "background: #fff" );
|
||||||
ui.applyStyle( "foreground: #fff" );
|
ui.applyStyle( "foreground: #fff" );
|
||||||
@@ -1234,15 +1244,19 @@ public class TestFlatStyling
|
|||||||
flatBorder( applyStyle );
|
flatBorder( applyStyle );
|
||||||
|
|
||||||
applyStyle.accept( "arc: 6" );
|
applyStyle.accept( "arc: 6" );
|
||||||
|
|
||||||
applyStyle.accept( "roundRect: true" );
|
applyStyle.accept( "roundRect: true" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void flatScrollPaneBorder( Consumer<String> applyStyle ) {
|
||||||
|
flatBorder( applyStyle );
|
||||||
|
|
||||||
|
applyStyle.accept( "arc: 6" );
|
||||||
|
}
|
||||||
|
|
||||||
private void flatTextBorder( Consumer<String> applyStyle ) {
|
private void flatTextBorder( Consumer<String> applyStyle ) {
|
||||||
flatBorder( applyStyle );
|
flatBorder( applyStyle );
|
||||||
|
|
||||||
applyStyle.accept( "arc: 6" );
|
applyStyle.accept( "arc: 6" );
|
||||||
|
|
||||||
applyStyle.accept( "roundRect: true" );
|
applyStyle.accept( "roundRect: true" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1283,12 +1297,20 @@ public class TestFlatStyling
|
|||||||
border.applyStyleProperty( "disabledBorderColor", Color.WHITE );
|
border.applyStyleProperty( "disabledBorderColor", Color.WHITE );
|
||||||
border.applyStyleProperty( "focusedBorderColor", Color.WHITE );
|
border.applyStyleProperty( "focusedBorderColor", Color.WHITE );
|
||||||
border.applyStyleProperty( "hoverBorderColor", Color.WHITE );
|
border.applyStyleProperty( "hoverBorderColor", Color.WHITE );
|
||||||
|
border.applyStyleProperty( "pressedBorderColor", Color.WHITE );
|
||||||
|
|
||||||
|
border.applyStyleProperty( "selectedBorderColor", Color.WHITE );
|
||||||
|
border.applyStyleProperty( "disabledSelectedBorderColor", Color.WHITE );
|
||||||
|
border.applyStyleProperty( "focusedSelectedBorderColor", Color.WHITE );
|
||||||
|
border.applyStyleProperty( "hoverSelectedBorderColor", Color.WHITE );
|
||||||
|
border.applyStyleProperty( "pressedSelectedBorderColor", Color.WHITE );
|
||||||
|
|
||||||
border.applyStyleProperty( "default.borderWidth", 2 );
|
border.applyStyleProperty( "default.borderWidth", 2 );
|
||||||
border.applyStyleProperty( "default.borderColor", Color.WHITE );
|
border.applyStyleProperty( "default.borderColor", Color.WHITE );
|
||||||
border.applyStyleProperty( "default.focusedBorderColor", Color.WHITE );
|
border.applyStyleProperty( "default.focusedBorderColor", Color.WHITE );
|
||||||
border.applyStyleProperty( "default.focusColor", Color.WHITE );
|
border.applyStyleProperty( "default.focusColor", Color.WHITE );
|
||||||
border.applyStyleProperty( "default.hoverBorderColor", Color.WHITE );
|
border.applyStyleProperty( "default.hoverBorderColor", Color.WHITE );
|
||||||
|
border.applyStyleProperty( "default.pressedBorderColor", Color.WHITE );
|
||||||
|
|
||||||
border.applyStyleProperty( "toolbar.focusWidth", 1.5f );
|
border.applyStyleProperty( "toolbar.focusWidth", 1.5f );
|
||||||
border.applyStyleProperty( "toolbar.focusColor", Color.WHITE );
|
border.applyStyleProperty( "toolbar.focusColor", Color.WHITE );
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import javax.swing.plaf.metal.MetalLookAndFeel;
|
|||||||
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
|
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
|
||||||
import com.formdev.flatlaf.*;
|
import com.formdev.flatlaf.*;
|
||||||
import com.formdev.flatlaf.extras.FlatAnimatedLafChange;
|
import com.formdev.flatlaf.extras.FlatAnimatedLafChange;
|
||||||
|
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
|
||||||
|
import com.formdev.flatlaf.themes.FlatMacLightLaf;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -66,6 +68,8 @@ class ControlBar
|
|||||||
lafModel.addElement( new LookAndFeelInfo( "FlatLaf Dark (F2)", FlatDarkLaf.class.getName() ) );
|
lafModel.addElement( new LookAndFeelInfo( "FlatLaf Dark (F2)", FlatDarkLaf.class.getName() ) );
|
||||||
lafModel.addElement( new LookAndFeelInfo( "FlatLaf IntelliJ (F3)", FlatIntelliJLaf.class.getName() ) );
|
lafModel.addElement( new LookAndFeelInfo( "FlatLaf IntelliJ (F3)", FlatIntelliJLaf.class.getName() ) );
|
||||||
lafModel.addElement( new LookAndFeelInfo( "FlatLaf Darcula (F4)", FlatDarculaLaf.class.getName() ) );
|
lafModel.addElement( new LookAndFeelInfo( "FlatLaf Darcula (F4)", FlatDarculaLaf.class.getName() ) );
|
||||||
|
lafModel.addElement( new LookAndFeelInfo( "FlatLaf macOS Light (F5)", FlatMacLightLaf.class.getName() ) );
|
||||||
|
lafModel.addElement( new LookAndFeelInfo( "FlatLaf macOS Dark (F6)", FlatMacDarkLaf.class.getName() ) );
|
||||||
|
|
||||||
UIManager.LookAndFeelInfo[] lookAndFeels = UIManager.getInstalledLookAndFeels();
|
UIManager.LookAndFeelInfo[] lookAndFeels = UIManager.getInstalledLookAndFeels();
|
||||||
for( UIManager.LookAndFeelInfo lookAndFeel : lookAndFeels ) {
|
for( UIManager.LookAndFeelInfo lookAndFeel : lookAndFeels ) {
|
||||||
@@ -127,6 +131,8 @@ class ControlBar
|
|||||||
registerSwitchToLookAndFeel( KeyEvent.VK_F2, FlatDarkLaf.class.getName() );
|
registerSwitchToLookAndFeel( KeyEvent.VK_F2, FlatDarkLaf.class.getName() );
|
||||||
registerSwitchToLookAndFeel( KeyEvent.VK_F3, FlatIntelliJLaf.class.getName() );
|
registerSwitchToLookAndFeel( KeyEvent.VK_F3, FlatIntelliJLaf.class.getName() );
|
||||||
registerSwitchToLookAndFeel( KeyEvent.VK_F4, FlatDarculaLaf.class.getName() );
|
registerSwitchToLookAndFeel( KeyEvent.VK_F4, FlatDarculaLaf.class.getName() );
|
||||||
|
registerSwitchToLookAndFeel( KeyEvent.VK_F5, FlatMacLightLaf.class.getName() );
|
||||||
|
registerSwitchToLookAndFeel( KeyEvent.VK_F6, FlatMacDarkLaf.class.getName() );
|
||||||
|
|
||||||
if( SystemInfo.isWindows )
|
if( SystemInfo.isWindows )
|
||||||
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );
|
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );
|
||||||
@@ -134,8 +140,8 @@ class ControlBar
|
|||||||
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.apple.laf.AquaLookAndFeel" );
|
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.apple.laf.AquaLookAndFeel" );
|
||||||
else if( SystemInfo.isLinux )
|
else if( SystemInfo.isLinux )
|
||||||
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" );
|
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" );
|
||||||
registerSwitchToLookAndFeel( KeyEvent.VK_F12, MetalLookAndFeel.class.getName() );
|
|
||||||
registerSwitchToLookAndFeel( KeyEvent.VK_F11, NimbusLookAndFeel.class.getName() );
|
registerSwitchToLookAndFeel( KeyEvent.VK_F11, NimbusLookAndFeel.class.getName() );
|
||||||
|
registerSwitchToLookAndFeel( KeyEvent.VK_F12, MetalLookAndFeel.class.getName() );
|
||||||
|
|
||||||
// register Alt+UP and Alt+DOWN to switch to previous/next theme
|
// register Alt+UP and Alt+DOWN to switch to previous/next theme
|
||||||
((JComponent)frame.getContentPane()).registerKeyboardAction(
|
((JComponent)frame.getContentPane()).registerKeyboardAction(
|
||||||
@@ -147,6 +153,9 @@ class ControlBar
|
|||||||
KeyStroke.getKeyStroke( KeyEvent.VK_DOWN, KeyEvent.ALT_DOWN_MASK ),
|
KeyStroke.getKeyStroke( KeyEvent.VK_DOWN, KeyEvent.ALT_DOWN_MASK ),
|
||||||
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
|
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
|
||||||
|
|
||||||
|
// register Alt+Shift+F1, F2, ... keys to change system scale factor
|
||||||
|
DemoPrefs.registerSystemScaleFactors( frame );
|
||||||
|
|
||||||
// register ESC key to close frame
|
// register ESC key to close frame
|
||||||
((JComponent)frame.getContentPane()).registerKeyboardAction(
|
((JComponent)frame.getContentPane()).registerKeyboardAction(
|
||||||
e -> {
|
e -> {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package com.formdev.flatlaf.demo;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.EventQueue;
|
|
||||||
import java.awt.datatransfer.DataFlavor;
|
import java.awt.datatransfer.DataFlavor;
|
||||||
import java.awt.datatransfer.StringSelection;
|
import java.awt.datatransfer.StringSelection;
|
||||||
import java.awt.datatransfer.Transferable;
|
import java.awt.datatransfer.Transferable;
|
||||||
@@ -26,6 +25,8 @@ import javax.swing.*;
|
|||||||
import javax.swing.table.*;
|
import javax.swing.table.*;
|
||||||
import javax.swing.tree.*;
|
import javax.swing.tree.*;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.util.ColorFunctions;
|
||||||
import net.miginfocom.swing.*;
|
import net.miginfocom.swing.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,10 +95,12 @@ class DataComponentsPanel
|
|||||||
|
|
||||||
private void rowSelectionChanged() {
|
private void rowSelectionChanged() {
|
||||||
table1.setRowSelectionAllowed( rowSelectionCheckBox.isSelected() );
|
table1.setRowSelectionAllowed( rowSelectionCheckBox.isSelected() );
|
||||||
|
roundedSelectionChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void columnSelectionChanged() {
|
private void columnSelectionChanged() {
|
||||||
table1.setColumnSelectionAllowed( columnSelectionCheckBox.isSelected() );
|
table1.setColumnSelectionAllowed( columnSelectionCheckBox.isSelected() );
|
||||||
|
roundedSelectionChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showHorizontalLinesChanged() {
|
private void showHorizontalLinesChanged() {
|
||||||
@@ -116,15 +119,38 @@ class DataComponentsPanel
|
|||||||
table1.setGridColor( redGridColorCheckBox.isSelected() ? Color.red : UIManager.getColor( "Table.gridColor" ) );
|
table1.setGridColor( redGridColorCheckBox.isSelected() ? Color.red : UIManager.getColor( "Table.gridColor" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void showHorizontalLinesPropertyChange() {
|
||||||
public void updateUI() {
|
showHorizontalLinesCheckBox.setSelected( table1.getShowHorizontalLines() );
|
||||||
super.updateUI();
|
}
|
||||||
|
|
||||||
EventQueue.invokeLater( () -> {
|
private void showVerticalLinesPropertyChange() {
|
||||||
showHorizontalLinesChanged();
|
showVerticalLinesCheckBox.setSelected( table1.getShowVerticalLines() );
|
||||||
showVerticalLinesChanged();
|
}
|
||||||
intercellSpacingChanged();
|
|
||||||
} );
|
private void intercellSpacingPropertyChange() {
|
||||||
|
intercellSpacingCheckBox.setSelected( table1.getRowMargin() != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void roundedSelectionChanged() {
|
||||||
|
String style = null;
|
||||||
|
if( roundedSelectionCheckBox.isSelected() ) {
|
||||||
|
style = rowSelectionCheckBox.isSelected()
|
||||||
|
? "selectionArc: 6; selectionInsets: 0,1,0,1"
|
||||||
|
: "selectionArc: 6";
|
||||||
|
}
|
||||||
|
table1.putClientProperty( FlatClientProperties.STYLE, style );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void alternatingRowsChanged() {
|
||||||
|
Color alternateRowColor = null;
|
||||||
|
if( alternatingRowsCheckBox.isSelected() ) {
|
||||||
|
Color background = table1.getBackground();
|
||||||
|
alternateRowColor = FlatLaf.isLafDark()
|
||||||
|
? ColorFunctions.lighten( background, 0.05f )
|
||||||
|
: ColorFunctions.darken( background, 0.05f );
|
||||||
|
}
|
||||||
|
UIManager.put( "Table.alternateRowColor", alternateRowColor );
|
||||||
|
table1.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings( { "unchecked", "rawtypes" } )
|
@SuppressWarnings( { "unchecked", "rawtypes" } )
|
||||||
@@ -151,12 +177,14 @@ class DataComponentsPanel
|
|||||||
JScrollPane scrollPane5 = new JScrollPane();
|
JScrollPane scrollPane5 = new JScrollPane();
|
||||||
table1 = new JTable();
|
table1 = new JTable();
|
||||||
JPanel tableOptionsPanel = new JPanel();
|
JPanel tableOptionsPanel = new JPanel();
|
||||||
|
roundedSelectionCheckBox = new JCheckBox();
|
||||||
showHorizontalLinesCheckBox = new JCheckBox();
|
showHorizontalLinesCheckBox = new JCheckBox();
|
||||||
showVerticalLinesCheckBox = new JCheckBox();
|
showVerticalLinesCheckBox = new JCheckBox();
|
||||||
intercellSpacingCheckBox = new JCheckBox();
|
intercellSpacingCheckBox = new JCheckBox();
|
||||||
redGridColorCheckBox = new JCheckBox();
|
redGridColorCheckBox = new JCheckBox();
|
||||||
rowSelectionCheckBox = new JCheckBox();
|
rowSelectionCheckBox = new JCheckBox();
|
||||||
columnSelectionCheckBox = new JCheckBox();
|
columnSelectionCheckBox = new JCheckBox();
|
||||||
|
alternatingRowsCheckBox = new JCheckBox();
|
||||||
dndCheckBox = new JCheckBox();
|
dndCheckBox = new JCheckBox();
|
||||||
JPopupMenu popupMenu2 = new JPopupMenu();
|
JPopupMenu popupMenu2 = new JPopupMenu();
|
||||||
JMenuItem menuItem3 = new JMenuItem();
|
JMenuItem menuItem3 = new JMenuItem();
|
||||||
@@ -333,10 +361,10 @@ class DataComponentsPanel
|
|||||||
"Not editable", "Text", "Combo", "Combo Editable", "Integer", "Boolean"
|
"Not editable", "Text", "Combo", "Combo Editable", "Integer", "Boolean"
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Class<?>[] columnTypes = new Class<?>[] {
|
Class<?>[] columnTypes = {
|
||||||
Object.class, Object.class, String.class, String.class, Integer.class, Boolean.class
|
Object.class, Object.class, String.class, String.class, Integer.class, Boolean.class
|
||||||
};
|
};
|
||||||
boolean[] columnEditable = new boolean[] {
|
boolean[] columnEditable = {
|
||||||
false, true, true, true, true, true
|
false, true, true, true, true, true
|
||||||
};
|
};
|
||||||
@Override
|
@Override
|
||||||
@@ -383,6 +411,9 @@ class DataComponentsPanel
|
|||||||
}
|
}
|
||||||
table1.setAutoCreateRowSorter(true);
|
table1.setAutoCreateRowSorter(true);
|
||||||
table1.setComponentPopupMenu(popupMenu2);
|
table1.setComponentPopupMenu(popupMenu2);
|
||||||
|
table1.addPropertyChangeListener("showHorizontalLines", e -> showHorizontalLinesPropertyChange());
|
||||||
|
table1.addPropertyChangeListener("showVerticalLines", e -> showVerticalLinesPropertyChange());
|
||||||
|
table1.addPropertyChangeListener("rowMargin", e -> intercellSpacingPropertyChange());
|
||||||
scrollPane5.setViewportView(table1);
|
scrollPane5.setViewportView(table1);
|
||||||
}
|
}
|
||||||
add(scrollPane5, "cell 1 3 3 1,width 300");
|
add(scrollPane5, "cell 1 3 3 1,width 300");
|
||||||
@@ -400,44 +431,56 @@ class DataComponentsPanel
|
|||||||
"[]0" +
|
"[]0" +
|
||||||
"[]0" +
|
"[]0" +
|
||||||
"[]0" +
|
"[]0" +
|
||||||
|
"[]0" +
|
||||||
|
"[]0" +
|
||||||
"[]0"));
|
"[]0"));
|
||||||
|
|
||||||
|
//---- roundedSelectionCheckBox ----
|
||||||
|
roundedSelectionCheckBox.setText("rounded selection");
|
||||||
|
roundedSelectionCheckBox.addActionListener(e -> roundedSelectionChanged());
|
||||||
|
tableOptionsPanel.add(roundedSelectionCheckBox, "cell 0 0");
|
||||||
|
|
||||||
//---- showHorizontalLinesCheckBox ----
|
//---- showHorizontalLinesCheckBox ----
|
||||||
showHorizontalLinesCheckBox.setText("show horizontal lines");
|
showHorizontalLinesCheckBox.setText("show horizontal lines");
|
||||||
showHorizontalLinesCheckBox.addActionListener(e -> showHorizontalLinesChanged());
|
showHorizontalLinesCheckBox.addActionListener(e -> showHorizontalLinesChanged());
|
||||||
tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 0");
|
tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 1");
|
||||||
|
|
||||||
//---- showVerticalLinesCheckBox ----
|
//---- showVerticalLinesCheckBox ----
|
||||||
showVerticalLinesCheckBox.setText("show vertical lines");
|
showVerticalLinesCheckBox.setText("show vertical lines");
|
||||||
showVerticalLinesCheckBox.addActionListener(e -> showVerticalLinesChanged());
|
showVerticalLinesCheckBox.addActionListener(e -> showVerticalLinesChanged());
|
||||||
tableOptionsPanel.add(showVerticalLinesCheckBox, "cell 0 1");
|
tableOptionsPanel.add(showVerticalLinesCheckBox, "cell 0 2");
|
||||||
|
|
||||||
//---- intercellSpacingCheckBox ----
|
//---- intercellSpacingCheckBox ----
|
||||||
intercellSpacingCheckBox.setText("intercell spacing");
|
intercellSpacingCheckBox.setText("intercell spacing");
|
||||||
intercellSpacingCheckBox.addActionListener(e -> intercellSpacingChanged());
|
intercellSpacingCheckBox.addActionListener(e -> intercellSpacingChanged());
|
||||||
tableOptionsPanel.add(intercellSpacingCheckBox, "cell 0 2");
|
tableOptionsPanel.add(intercellSpacingCheckBox, "cell 0 3");
|
||||||
|
|
||||||
//---- redGridColorCheckBox ----
|
//---- redGridColorCheckBox ----
|
||||||
redGridColorCheckBox.setText("red grid color");
|
redGridColorCheckBox.setText("red grid color");
|
||||||
redGridColorCheckBox.addActionListener(e -> redGridColorChanged());
|
redGridColorCheckBox.addActionListener(e -> redGridColorChanged());
|
||||||
tableOptionsPanel.add(redGridColorCheckBox, "cell 0 3");
|
tableOptionsPanel.add(redGridColorCheckBox, "cell 0 4");
|
||||||
|
|
||||||
//---- rowSelectionCheckBox ----
|
//---- rowSelectionCheckBox ----
|
||||||
rowSelectionCheckBox.setText("row selection");
|
rowSelectionCheckBox.setText("row selection");
|
||||||
rowSelectionCheckBox.setSelected(true);
|
rowSelectionCheckBox.setSelected(true);
|
||||||
rowSelectionCheckBox.addActionListener(e -> rowSelectionChanged());
|
rowSelectionCheckBox.addActionListener(e -> rowSelectionChanged());
|
||||||
tableOptionsPanel.add(rowSelectionCheckBox, "cell 0 4");
|
tableOptionsPanel.add(rowSelectionCheckBox, "cell 0 5");
|
||||||
|
|
||||||
//---- columnSelectionCheckBox ----
|
//---- columnSelectionCheckBox ----
|
||||||
columnSelectionCheckBox.setText("column selection");
|
columnSelectionCheckBox.setText("column selection");
|
||||||
columnSelectionCheckBox.addActionListener(e -> columnSelectionChanged());
|
columnSelectionCheckBox.addActionListener(e -> columnSelectionChanged());
|
||||||
tableOptionsPanel.add(columnSelectionCheckBox, "cell 0 5");
|
tableOptionsPanel.add(columnSelectionCheckBox, "cell 0 6");
|
||||||
|
|
||||||
|
//---- alternatingRowsCheckBox ----
|
||||||
|
alternatingRowsCheckBox.setText("alternating rows");
|
||||||
|
alternatingRowsCheckBox.addActionListener(e -> alternatingRowsChanged());
|
||||||
|
tableOptionsPanel.add(alternatingRowsCheckBox, "cell 0 7");
|
||||||
|
|
||||||
//---- dndCheckBox ----
|
//---- dndCheckBox ----
|
||||||
dndCheckBox.setText("enable drag and drop");
|
dndCheckBox.setText("enable drag and drop");
|
||||||
dndCheckBox.setMnemonic('D');
|
dndCheckBox.setMnemonic('D');
|
||||||
dndCheckBox.addActionListener(e -> dndChanged());
|
dndCheckBox.addActionListener(e -> dndChanged());
|
||||||
tableOptionsPanel.add(dndCheckBox, "cell 0 6");
|
tableOptionsPanel.add(dndCheckBox, "cell 0 8");
|
||||||
}
|
}
|
||||||
add(tableOptionsPanel, "cell 4 3");
|
add(tableOptionsPanel, "cell 4 3");
|
||||||
|
|
||||||
@@ -474,12 +517,14 @@ class DataComponentsPanel
|
|||||||
private JTree tree3;
|
private JTree tree3;
|
||||||
private JTree tree2;
|
private JTree tree2;
|
||||||
private JTable table1;
|
private JTable table1;
|
||||||
|
private JCheckBox roundedSelectionCheckBox;
|
||||||
private JCheckBox showHorizontalLinesCheckBox;
|
private JCheckBox showHorizontalLinesCheckBox;
|
||||||
private JCheckBox showVerticalLinesCheckBox;
|
private JCheckBox showVerticalLinesCheckBox;
|
||||||
private JCheckBox intercellSpacingCheckBox;
|
private JCheckBox intercellSpacingCheckBox;
|
||||||
private JCheckBox redGridColorCheckBox;
|
private JCheckBox redGridColorCheckBox;
|
||||||
private JCheckBox rowSelectionCheckBox;
|
private JCheckBox rowSelectionCheckBox;
|
||||||
private JCheckBox columnSelectionCheckBox;
|
private JCheckBox columnSelectionCheckBox;
|
||||||
|
private JCheckBox alternatingRowsCheckBox;
|
||||||
private JCheckBox dndCheckBox;
|
private JCheckBox dndCheckBox;
|
||||||
// JFormDesigner - End of variables declaration //GEN-END:variables
|
// JFormDesigner - End of variables declaration //GEN-END:variables
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
JFDML JFormDesigner: "8.0.0.0.194" Java: "17.0.2" encoding: "UTF-8"
|
JFDML JFormDesigner: "8.2.0.0.331" Java: "21" encoding: "UTF-8"
|
||||||
|
|
||||||
new FormModel {
|
new FormModel {
|
||||||
contentType: "form/swing"
|
contentType: "form/swing"
|
||||||
@@ -333,6 +333,9 @@ new FormModel {
|
|||||||
auxiliary() {
|
auxiliary() {
|
||||||
"JavaCodeGenerator.variableLocal": false
|
"JavaCodeGenerator.variableLocal": false
|
||||||
}
|
}
|
||||||
|
addEvent( new FormEvent( "java.beans.PropertyChangeListener", "propertyChange", "showHorizontalLinesPropertyChange", false, "showHorizontalLines" ) )
|
||||||
|
addEvent( new FormEvent( "java.beans.PropertyChangeListener", "propertyChange", "showVerticalLinesPropertyChange", false, "showVerticalLines" ) )
|
||||||
|
addEvent( new FormEvent( "java.beans.PropertyChangeListener", "propertyChange", "intercellSpacingPropertyChange", false, "rowMargin" ) )
|
||||||
} )
|
} )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 1 3 3 1,width 300"
|
"value": "cell 1 3 3 1,width 300"
|
||||||
@@ -340,9 +343,19 @@ new FormModel {
|
|||||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||||
"$layoutConstraints": "insets 0,hidemode 3"
|
"$layoutConstraints": "insets 0,hidemode 3"
|
||||||
"$columnConstraints": "[]"
|
"$columnConstraints": "[]"
|
||||||
"$rowConstraints": "[]0[]0[]0[]0[]0[]0[]0"
|
"$rowConstraints": "[]0[]0[]0[]0[]0[]0[]0[]0[]0"
|
||||||
} ) {
|
} ) {
|
||||||
name: "tableOptionsPanel"
|
name: "tableOptionsPanel"
|
||||||
|
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||||
|
name: "roundedSelectionCheckBox"
|
||||||
|
"text": "rounded selection"
|
||||||
|
auxiliary() {
|
||||||
|
"JavaCodeGenerator.variableLocal": false
|
||||||
|
}
|
||||||
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 0 0"
|
||||||
|
} )
|
||||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||||
name: "showHorizontalLinesCheckBox"
|
name: "showHorizontalLinesCheckBox"
|
||||||
"text": "show horizontal lines"
|
"text": "show horizontal lines"
|
||||||
@@ -351,7 +364,7 @@ new FormModel {
|
|||||||
}
|
}
|
||||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showHorizontalLinesChanged", false ) )
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showHorizontalLinesChanged", false ) )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 0"
|
"value": "cell 0 1"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||||
name: "showVerticalLinesCheckBox"
|
name: "showVerticalLinesCheckBox"
|
||||||
@@ -361,7 +374,7 @@ new FormModel {
|
|||||||
}
|
}
|
||||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showVerticalLinesChanged", false ) )
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showVerticalLinesChanged", false ) )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 1"
|
"value": "cell 0 2"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||||
name: "intercellSpacingCheckBox"
|
name: "intercellSpacingCheckBox"
|
||||||
@@ -371,7 +384,7 @@ new FormModel {
|
|||||||
}
|
}
|
||||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "intercellSpacingChanged", false ) )
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "intercellSpacingChanged", false ) )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 2"
|
"value": "cell 0 3"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||||
name: "redGridColorCheckBox"
|
name: "redGridColorCheckBox"
|
||||||
@@ -381,7 +394,7 @@ new FormModel {
|
|||||||
}
|
}
|
||||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "redGridColorChanged", false ) )
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "redGridColorChanged", false ) )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 3"
|
"value": "cell 0 4"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||||
name: "rowSelectionCheckBox"
|
name: "rowSelectionCheckBox"
|
||||||
@@ -392,7 +405,7 @@ new FormModel {
|
|||||||
}
|
}
|
||||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "rowSelectionChanged", false ) )
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "rowSelectionChanged", false ) )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 4"
|
"value": "cell 0 5"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||||
name: "columnSelectionCheckBox"
|
name: "columnSelectionCheckBox"
|
||||||
@@ -402,7 +415,17 @@ new FormModel {
|
|||||||
}
|
}
|
||||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "columnSelectionChanged", false ) )
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "columnSelectionChanged", false ) )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 5"
|
"value": "cell 0 6"
|
||||||
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||||
|
name: "alternatingRowsCheckBox"
|
||||||
|
"text": "alternating rows"
|
||||||
|
auxiliary() {
|
||||||
|
"JavaCodeGenerator.variableLocal": false
|
||||||
|
}
|
||||||
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "alternatingRowsChanged", false ) )
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 0 7"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||||
name: "dndCheckBox"
|
name: "dndCheckBox"
|
||||||
@@ -413,7 +436,7 @@ new FormModel {
|
|||||||
}
|
}
|
||||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dndChanged", false ) )
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dndChanged", false ) )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 6"
|
"value": "cell 0 8"
|
||||||
} )
|
} )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 4 3"
|
"value": "cell 4 3"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user