mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 06:27:13 -06:00
Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
31
.github/workflows/ci.yml
vendored
31
.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:
|
||||||
@@ -24,22 +32,23 @@ jobs:
|
|||||||
- 8
|
- 8
|
||||||
- 11 # LTS
|
- 11 # LTS
|
||||||
- 17 # LTS
|
- 17 # LTS
|
||||||
|
- 21 # LTS
|
||||||
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 +59,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 +79,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 +115,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
|
||||||
|
|||||||
10
.github/workflows/natives.yml
vendored
10
.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:
|
||||||
@@ -26,12 +28,12 @@ jobs:
|
|||||||
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
|
||||||
@@ -43,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 }}
|
||||||
139
CHANGELOG.md
139
CHANGELOG.md
@@ -1,6 +1,141 @@
|
|||||||
FlatLaf Change Log
|
FlatLaf Change Log
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
## 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
|
## 3.3
|
||||||
|
|
||||||
#### New features and improvements
|
#### New features and improvements
|
||||||
@@ -19,7 +154,7 @@ FlatLaf Change Log
|
|||||||
#### Fixed bugs
|
#### Fixed bugs
|
||||||
|
|
||||||
- Button and ToggleButton: Selected buttons did not use explicitly set
|
- Button and ToggleButton: Selected buttons did not use explicitly set
|
||||||
foreground color. (issue 756)
|
foreground color. (issue #756)
|
||||||
- FileChooser: Catch NPE in Java 21 when getting icon for `.exe` files that use
|
- FileChooser: Catch NPE in Java 21 when getting icon for `.exe` files that use
|
||||||
default Windows exe icon. (see
|
default Windows exe icon. (see
|
||||||
[JDK-8320692](https://bugs.openjdk.org/browse/JDK-8320692))
|
[JDK-8320692](https://bugs.openjdk.org/browse/JDK-8320692))
|
||||||
@@ -772,7 +907,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.
|
||||||
|
|||||||
@@ -311,6 +311,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
|
||||||
|
|
||||||
|
|||||||
@@ -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,6 +148,8 @@ 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" ),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#Signature file v4.1
|
#Signature file v4.1
|
||||||
#Version 3.3
|
#Version 3.5.1
|
||||||
|
|
||||||
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"
|
||||||
@@ -290,6 +296,7 @@ fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flat
|
|||||||
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()
|
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_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"
|
||||||
@@ -623,16 +630,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)
|
||||||
@@ -1131,6 +1155,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
|
||||||
|
|||||||
@@ -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 --------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -388,6 +488,46 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
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>
|
||||||
|
* <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}.
|
||||||
@@ -847,7 +987,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),
|
||||||
@@ -959,7 +1099,7 @@ public interface FlatClientProperties
|
|||||||
* <strong>Allowed Values</strong>
|
* <strong>Allowed Values</strong>
|
||||||
* {@link SwingConstants#LEFT},
|
* {@link SwingConstants#LEFT},
|
||||||
* {@link SwingConstants#RIGHT},
|
* {@link SwingConstants#RIGHT},
|
||||||
* {@link #TABBED_PANE_TAB_ROTATION_NONE}, (default)
|
* {@link #TABBED_PANE_TAB_ROTATION_NONE} (default),
|
||||||
* {@link #TABBED_PANE_TAB_ROTATION_AUTO},
|
* {@link #TABBED_PANE_TAB_ROTATION_AUTO},
|
||||||
* {@link #TABBED_PANE_TAB_ROTATION_LEFT} or
|
* {@link #TABBED_PANE_TAB_ROTATION_LEFT} or
|
||||||
* {@link #TABBED_PANE_TAB_ROTATION_RIGHT}
|
* {@link #TABBED_PANE_TAB_ROTATION_RIGHT}
|
||||||
@@ -1208,8 +1348,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}
|
||||||
@@ -1263,6 +1403,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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -172,24 +172,47 @@ 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 be 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}.
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -173,8 +178,29 @@ debug*/
|
|||||||
targetBottomY = popupLocation.y + popupSize.height;
|
targetBottomY = popupLocation.y + popupSize.height;
|
||||||
|
|
||||||
// install own event queue to suppress 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 );
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -135,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();
|
||||||
}
|
}
|
||||||
@@ -277,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;
|
||||||
|
|
||||||
@@ -906,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();
|
||||||
@@ -918,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ) );
|
||||||
@@ -401,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;
|
||||||
|
|
||||||
@@ -420,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 );
|
||||||
@@ -429,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 );
|
||||||
|
|
||||||
@@ -461,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 );
|
||||||
@@ -487,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 );
|
||||||
@@ -498,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 );
|
||||||
@@ -592,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() ) {
|
||||||
@@ -600,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 ) {
|
||||||
|
|||||||
@@ -23,13 +23,20 @@ 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.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
|
||||||
*/
|
*/
|
||||||
@@ -41,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 */
|
||||||
@@ -52,15 +59,28 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -70,11 +90,31 @@ public class FlatLineBorder
|
|||||||
if( c instanceof JComponent && ((JComponent)c).getClientProperty( FlatPopupFactory.KEY_POPUP_USES_NATIVE_BORDER ) != null )
|
if( c instanceof JComponent && ((JComponent)c).getClientProperty( FlatPopupFactory.KEY_POPUP_USES_NATIVE_BORDER ) != null )
|
||||||
return;
|
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 );
|
||||||
@@ -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;
|
||||||
@@ -103,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 ) {
|
||||||
@@ -117,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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,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();
|
||||||
@@ -168,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,6 +35,8 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
*/
|
*/
|
||||||
class FlatNativeLinuxLibrary
|
class FlatNativeLinuxLibrary
|
||||||
{
|
{
|
||||||
|
private static int API_VERSION_LINUX = 3001;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether native library is loaded/available.
|
* Checks whether native library is loaded/available.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -42,7 +44,7 @@ class FlatNativeLinuxLibrary
|
|||||||
* method of this class. Otherwise, the native library may not be loaded.
|
* 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
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Rectangle;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Native methods for macOS.
|
* Native methods for macOS.
|
||||||
@@ -42,6 +44,8 @@ import java.awt.Window;
|
|||||||
*/
|
*/
|
||||||
public class FlatNativeMacLibrary
|
public class FlatNativeMacLibrary
|
||||||
{
|
{
|
||||||
|
private static int API_VERSION_MACOS = 2001;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether native library is loaded/available.
|
* Checks whether native library is loaded/available.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -49,8 +53,19 @@ public class FlatNativeMacLibrary
|
|||||||
* method of this class. Otherwise, the native library may not be loaded.
|
* method of this class. Otherwise, the native library may not be loaded.
|
||||||
*/
|
*/
|
||||||
public static boolean isLoaded() {
|
public static boolean isLoaded() {
|
||||||
return FlatNativeLibrary.isLoaded();
|
return SystemInfo.isMacOS && FlatNativeLibrary.isLoaded( API_VERSION_MACOS );
|
||||||
}
|
}
|
||||||
|
|
||||||
public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor );
|
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 );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ import java.awt.Component;
|
|||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Point;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Toolkit;
|
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;
|
||||||
@@ -218,13 +219,13 @@ 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( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots,
|
nativeProvider.updateTitleBarInfo( window, titleBarHeight, captionHitTestCallback,
|
||||||
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
|
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,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 );
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +41,7 @@ public class FlatNativeWindowsLibrary
|
|||||||
* method of this class. Otherwise, the native library may not be loaded.
|
* 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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -41,6 +42,7 @@ 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 javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLayeredPane;
|
import javax.swing.JLayeredPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@@ -76,6 +78,8 @@ public class FlatPopupFactory
|
|||||||
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
|
||||||
@@ -88,15 +92,34 @@ 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 ) {
|
||||||
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 && SystemInfo.isMacOS && FlatNativeMacLibrary.isLoaded() )
|
if( popup.popupWindow != null && SystemInfo.isMacOS &&
|
||||||
|
// do not use rounded border on macOS 14.4+ because it may freeze the application
|
||||||
|
// and crash the macOS WindowServer process (reports vary from Finder restarts to OS restarts)
|
||||||
|
// https://github.com/apache/netbeans/issues/7560#issuecomment-2226439215
|
||||||
|
// https://github.com/apache/netbeans/issues/6647#issuecomment-2070124442
|
||||||
|
SystemInfo.osVersion < SystemInfo.toVersion( 14, 4, 0, 0 ) &&
|
||||||
|
FlatNativeMacLibrary.isLoaded() )
|
||||||
setupRoundedBorder( popup.popupWindow, owner, contents );
|
setupRoundedBorder( popup.popupWindow, owner, contents );
|
||||||
return popup;
|
return popup;
|
||||||
}
|
}
|
||||||
@@ -105,7 +128,7 @@ public class FlatPopupFactory
|
|||||||
if( isWindows11BorderSupported() &&
|
if( isWindows11BorderSupported() &&
|
||||||
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 )
|
||||||
setupRoundedBorder( popup.popupWindow, owner, contents );
|
setupRoundedBorder( popup.popupWindow, owner, contents );
|
||||||
return popup;
|
return popup;
|
||||||
@@ -116,7 +139,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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -227,6 +253,24 @@ public class FlatPopupFactory
|
|||||||
return UIManager.get( uiKey );
|
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 -----------------------------------------------------------
|
//---- tooltips -----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -490,18 +534,31 @@ public class FlatPopupFactory
|
|||||||
|
|
||||||
//---- 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 );
|
||||||
@@ -515,8 +572,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 );
|
||||||
|
|
||||||
@@ -540,13 +616,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 )
|
if( contents instanceof JComponent )
|
||||||
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, null );
|
((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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,6 +656,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 ----------------------------------------------
|
||||||
@@ -564,8 +687,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;
|
||||||
@@ -580,11 +701,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 )
|
||||||
@@ -600,24 +721,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 ) );
|
||||||
@@ -654,6 +775,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" ),
|
||||||
@@ -662,14 +800,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 ) {
|
||||||
@@ -680,10 +818,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 ) {
|
||||||
@@ -692,7 +831,7 @@ public class FlatPopupFactory
|
|||||||
mediumWeightPanel = null;
|
mediumWeightPanel = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.hide();
|
super.hideImpl();
|
||||||
|
|
||||||
if( dropShadowWindow != null ) {
|
if( dropShadowWindow != null ) {
|
||||||
dropShadowWindow.setBackground( oldDropShadowWindowBackground );
|
dropShadowWindow.setBackground( oldDropShadowWindowBackground );
|
||||||
@@ -776,5 +915,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -50,6 +47,7 @@ import javax.swing.plaf.RootPaneUI;
|
|||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicRootPaneUI;
|
import javax.swing.plaf.basic.BasicRootPaneUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -87,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();
|
||||||
@@ -106,6 +104,7 @@ public class FlatRootPaneUI
|
|||||||
installBorder();
|
installBorder();
|
||||||
|
|
||||||
installNativeWindowBorder();
|
installNativeWindowBorder();
|
||||||
|
macInstallFullWindowContentSupport();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void installBorder() {
|
protected void installBorder() {
|
||||||
@@ -122,6 +121,7 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
uninstallNativeWindowBorder();
|
uninstallNativeWindowBorder();
|
||||||
uninstallClientDecorations();
|
uninstallClientDecorations();
|
||||||
|
macUninstallFullWindowContentSupport();
|
||||||
rootPane = null;
|
rootPane = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +155,8 @@ public class FlatRootPaneUI
|
|||||||
if( background == null || background instanceof UIResource )
|
if( background == null || background instanceof UIResource )
|
||||||
parent.setBackground( UIManager.getColor( "control" ) );
|
parent.setBackground( UIManager.getColor( "control" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macClearBackgroundForTranslucentWindow( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -174,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 */
|
||||||
@@ -302,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 );
|
||||||
@@ -359,6 +448,28 @@ public class FlatRootPaneUI
|
|||||||
titlePane.titleBarColorsChanged();
|
titlePane.titleBarColorsChanged();
|
||||||
break;
|
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;
|
||||||
@@ -367,14 +478,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 */
|
||||||
@@ -410,23 +550,21 @@ 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 );
|
||||||
|
height += menuBarSize.height;
|
||||||
width = Math.max( width, menuBarSize.width );
|
}
|
||||||
height += menuBarSize.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Insets insets = rootPane.getInsets();
|
Insets insets = rootPane.getInsets();
|
||||||
@@ -451,12 +589,29 @@ 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;
|
||||||
titlePane.setBounds( 0, 0, width, prefHeight );
|
boolean isFullWindowContent = isFullWindowContent( rootPane );
|
||||||
nextY += prefHeight;
|
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.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// glass pane
|
// glass pane
|
||||||
@@ -467,7 +622,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();
|
||||||
@@ -475,13 +630,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 ) );
|
||||||
|
|||||||
@@ -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":
|
||||||
@@ -492,7 +493,7 @@ public class FlatScrollBarUI
|
|||||||
|
|
||||||
private void repaint() {
|
private void repaint() {
|
||||||
if( scrollbar.isEnabled() )
|
if( scrollbar.isEnabled() )
|
||||||
scrollbar.repaint();
|
HiDPIUtils.repaint( scrollbar );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ 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;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -297,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;
|
||||||
|
|
||||||
@@ -321,14 +322,14 @@ 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;
|
break;
|
||||||
|
|
||||||
case "border":
|
case "border":
|
||||||
@@ -339,7 +340,7 @@ public class FlatScrollPaneUI
|
|||||||
borderShared = null;
|
borderShared = null;
|
||||||
installStyle();
|
installStyle();
|
||||||
scrollpane.revalidate();
|
scrollpane.revalidate();
|
||||||
scrollpane.repaint();
|
HiDPIUtils.repaint( scrollpane );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -538,14 +539,14 @@ public class FlatScrollPaneUI
|
|||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
// necessary to update focus border
|
// necessary to update focus border
|
||||||
if( scrollpane.getBorder() instanceof FlatBorder )
|
if( scrollpane.getBorder() instanceof FlatBorder )
|
||||||
scrollpane.repaint();
|
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
|
||||||
if( scrollpane.getBorder() instanceof FlatBorder )
|
if( scrollpane.getBorder() instanceof FlatBorder )
|
||||||
scrollpane.repaint();
|
HiDPIUtils.repaint( scrollpane );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 ) {
|
||||||
|
// set new thumb location and compute union of old and new thumb bounds
|
||||||
|
Rectangle r = new Rectangle( thumbRect );
|
||||||
|
thumbRect.setLocation( x, y );
|
||||||
|
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
|
||||||
|
|
||||||
if( !isRoundThumb() ) {
|
if( !isRoundThumb() ) {
|
||||||
// the needle of the directional thumb is painted outside of thumbRect
|
// the needle of the directional thumb is painted outside of thumbRect
|
||||||
// --> must increase repaint rectangle
|
// --> must increase repaint rectangle
|
||||||
|
|
||||||
// set new thumb location and compute union of old and new thumb bounds
|
|
||||||
Rectangle r = new Rectangle( thumbRect );
|
|
||||||
thumbRect.setLocation( x, y );
|
|
||||||
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
|
|
||||||
|
|
||||||
// increase union rectangle for repaint
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ 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;
|
/** @since 3.3 */ @Styleable protected Color draggingColor;
|
||||||
@@ -227,6 +227,15 @@ public class FlatSplitPaneUI
|
|||||||
((FlatSplitPaneDivider)divider).paintStyle( g, x, y, width, height );
|
((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
|
||||||
@@ -292,6 +301,10 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ 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.Animator;
|
import com.formdev.flatlaf.util.Animator;
|
||||||
import com.formdev.flatlaf.util.CubicBezierEasing;
|
import com.formdev.flatlaf.util.CubicBezierEasing;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.JavaCompatibility;
|
import com.formdev.flatlaf.util.JavaCompatibility;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
@@ -182,7 +183,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatTabbedPaneUI
|
public class FlatTabbedPaneUI
|
||||||
extends BasicTabbedPaneUI
|
extends BasicTabbedPaneUI
|
||||||
implements StyleableUI
|
implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
|
||||||
{
|
{
|
||||||
// tab type
|
// tab type
|
||||||
/** @since 2 */ protected static final int TAB_TYPE_UNDERLINED = 0;
|
/** @since 2 */ protected static final int TAB_TYPE_UNDERLINED = 0;
|
||||||
@@ -895,7 +896,7 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tabPane.repaint( r );
|
HiDPIUtils.repaint( tabPane, r );
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean inCalculateEqual;
|
private boolean inCalculateEqual;
|
||||||
@@ -1684,7 +1685,7 @@ debug*/
|
|||||||
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
|
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
|
||||||
|
|
||||||
// add gap for selected tab to path
|
// add gap for selected tab to path
|
||||||
if( getTabType() == TAB_TYPE_CARD ) {
|
if( getTabType() == TAB_TYPE_CARD && selectedIndex >= 0 ) {
|
||||||
float csh = scale( (float) contentSeparatorHeight );
|
float csh = scale( (float) contentSeparatorHeight );
|
||||||
|
|
||||||
Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
|
Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
|
||||||
@@ -2300,6 +2301,17 @@ debug*/
|
|||||||
return (rects[last].y + rects[last].height) - rects[0].y;
|
return (rects[last].y + rects[last].height) - rects[0].y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
|
||||||
|
|
||||||
|
/** @since 3.4 */
|
||||||
|
@Override
|
||||||
|
public Boolean isTitleBarCaptionAt( int x, int y ) {
|
||||||
|
if( tabForCoordinate( tabPane, x, y ) >= 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return null; // check children
|
||||||
|
}
|
||||||
|
|
||||||
//---- class TabCloseButton -----------------------------------------------
|
//---- class TabCloseButton -----------------------------------------------
|
||||||
|
|
||||||
private static class TabCloseButton
|
private static class TabCloseButton
|
||||||
@@ -2570,19 +2582,19 @@ debug*/
|
|||||||
@Override
|
@Override
|
||||||
public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {
|
public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {
|
||||||
popupVisible = true;
|
popupVisible = true;
|
||||||
repaint();
|
HiDPIUtils.repaint( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {
|
public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {
|
||||||
popupVisible = false;
|
popupVisible = false;
|
||||||
repaint();
|
HiDPIUtils.repaint( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void popupMenuCanceled( PopupMenuEvent e ) {
|
public void popupMenuCanceled( PopupMenuEvent e ) {
|
||||||
popupVisible = false;
|
popupVisible = false;
|
||||||
repaint();
|
HiDPIUtils.repaint( this );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3091,7 +3103,7 @@ debug*/
|
|||||||
|
|
||||||
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
||||||
case TABBED_PANE_TAB_TYPE:
|
case TABBED_PANE_TAB_TYPE:
|
||||||
tabPane.repaint();
|
HiDPIUtils.repaint( tabPane );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
|
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
|
||||||
@@ -3114,14 +3126,14 @@ debug*/
|
|||||||
case TABBED_PANE_TAB_ICON_PLACEMENT:
|
case TABBED_PANE_TAB_ICON_PLACEMENT:
|
||||||
case TABBED_PANE_TAB_CLOSABLE:
|
case TABBED_PANE_TAB_CLOSABLE:
|
||||||
tabPane.revalidate();
|
tabPane.revalidate();
|
||||||
tabPane.repaint();
|
HiDPIUtils.repaint( tabPane );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TABBED_PANE_LEADING_COMPONENT:
|
case TABBED_PANE_LEADING_COMPONENT:
|
||||||
uninstallLeadingComponent();
|
uninstallLeadingComponent();
|
||||||
installLeadingComponent();
|
installLeadingComponent();
|
||||||
tabPane.revalidate();
|
tabPane.revalidate();
|
||||||
tabPane.repaint();
|
HiDPIUtils.repaint( tabPane );
|
||||||
ensureSelectedTabIsVisibleLater();
|
ensureSelectedTabIsVisibleLater();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -3129,7 +3141,7 @@ debug*/
|
|||||||
uninstallTrailingComponent();
|
uninstallTrailingComponent();
|
||||||
installTrailingComponent();
|
installTrailingComponent();
|
||||||
tabPane.revalidate();
|
tabPane.revalidate();
|
||||||
tabPane.repaint();
|
HiDPIUtils.repaint( tabPane );
|
||||||
ensureSelectedTabIsVisibleLater();
|
ensureSelectedTabIsVisibleLater();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -3137,7 +3149,7 @@ debug*/
|
|||||||
case STYLE_CLASS:
|
case STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
tabPane.revalidate();
|
tabPane.revalidate();
|
||||||
tabPane.repaint();
|
HiDPIUtils.repaint( tabPane );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3161,7 +3173,7 @@ debug*/
|
|||||||
case TABBED_PANE_TAB_ALIGNMENT:
|
case TABBED_PANE_TAB_ALIGNMENT:
|
||||||
case TABBED_PANE_TAB_CLOSABLE:
|
case TABBED_PANE_TAB_CLOSABLE:
|
||||||
tabPane.revalidate();
|
tabPane.revalidate();
|
||||||
tabPane.repaint();
|
HiDPIUtils.repaint( tabPane );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3756,9 +3768,20 @@ debug*/
|
|||||||
boolean hideDisabledScrollButtons = (scrollButtonsPolicy == AS_NEEDED_SINGLE && scrollButtonsPlacement == BOTH);
|
boolean hideDisabledScrollButtons = (scrollButtonsPolicy == AS_NEEDED_SINGLE && scrollButtonsPlacement == BOTH);
|
||||||
boolean trailingScrollButtons = (scrollButtonsPlacement == TRAILING);
|
boolean trailingScrollButtons = (scrollButtonsPlacement == TRAILING);
|
||||||
|
|
||||||
// for right-to-left always use "more tabs" button for horizontal scrolling
|
// For right-to-left, always use "more tabs" button for horizontal scrolling
|
||||||
// because methods scrollForward() and scrollBackward() in class
|
// because methods scrollForward() and scrollBackward() in class
|
||||||
// BasicTabbedPaneUI.ScrollableTabSupport do not work for right-to-left
|
// BasicTabbedPaneUI.ScrollableTabSupport do not work for right-to-left.
|
||||||
|
//
|
||||||
|
// One problem is that BasicTabbedPaneUI.getClosestTab(), which is used
|
||||||
|
// to compute leadingTabIndex, does not work for right-to-left because is uses "binary" search
|
||||||
|
// on rects[] to find tab, but rects[] is ordered in reverse order for right-to-left.
|
||||||
|
// So leadingTabIndex is either zero or tabCount.
|
||||||
|
// Therefore increasing/decreasing leadingTabIndex in scrollForward()
|
||||||
|
// and scrollBackward() does not work as expected.
|
||||||
|
// Also backward/forward scroll buttons are not correctly enabled/disabled.
|
||||||
|
//
|
||||||
|
// Fixing this would require replacing nearly whole functionality of class
|
||||||
|
// BasicTabbedPaneUI.ScrollableTabSupport, which is not possible because it is private.
|
||||||
boolean leftToRight = isLeftToRight();
|
boolean leftToRight = isLeftToRight();
|
||||||
if( !leftToRight && isHorizontalTabPlacement( tabPane.getTabPlacement() ) ) {
|
if( !leftToRight && isHorizontalTabPlacement( tabPane.getTabPlacement() ) ) {
|
||||||
useMoreTabsButton = true;
|
useMoreTabsButton = true;
|
||||||
@@ -3840,6 +3863,8 @@ debug*/
|
|||||||
w -= buttonWidth;
|
w -= buttonWidth;
|
||||||
moreTabsButtonVisible = true;
|
moreTabsButtonVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// layout scroll buttons
|
||||||
if( useScrollButtons ) {
|
if( useScrollButtons ) {
|
||||||
// the tabViewport view size is set in
|
// the tabViewport view size is set in
|
||||||
// BasicTabbedPaneUI.TabbedPaneScrollLayout.calculateTabRects(),
|
// BasicTabbedPaneUI.TabbedPaneScrollLayout.calculateTabRects(),
|
||||||
@@ -3847,30 +3872,28 @@ debug*/
|
|||||||
Point viewPosition = tabViewport.getViewPosition();
|
Point viewPosition = tabViewport.getViewPosition();
|
||||||
Dimension viewSize = tabViewport.getViewSize();
|
Dimension viewSize = tabViewport.getViewSize();
|
||||||
|
|
||||||
|
// layout forward button on trailing side
|
||||||
|
if( !hideDisabledScrollButtons || viewSize.width - viewPosition.x > w ) {
|
||||||
|
int buttonWidth = forwardButton.getPreferredSize().width;
|
||||||
|
forwardButton.setBounds( x + w - buttonWidth, y, buttonWidth, h );
|
||||||
|
w -= buttonWidth;
|
||||||
|
forwardButtonVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
// layout backward button
|
// layout backward button
|
||||||
if( !hideDisabledScrollButtons || viewPosition.x > 0 ) {
|
if( !hideDisabledScrollButtons || viewPosition.x > 0 ) {
|
||||||
int buttonWidth = backwardButton.getPreferredSize().width;
|
int buttonWidth = backwardButton.getPreferredSize().width;
|
||||||
if( trailingScrollButtons ) {
|
if( trailingScrollButtons ) {
|
||||||
// on trailing side
|
// on trailing side
|
||||||
backwardButton.setBounds( leftToRight ? (x + w - buttonWidth) : x, y, buttonWidth, h );
|
backwardButton.setBounds( x + w - buttonWidth, y, buttonWidth, h );
|
||||||
x += leftToRight ? 0 : buttonWidth;
|
|
||||||
} else {
|
} else {
|
||||||
// on leading side
|
// on leading side
|
||||||
backwardButton.setBounds( leftToRight ? x : (x + w - buttonWidth), y, buttonWidth, h );
|
backwardButton.setBounds( x, y, buttonWidth, h );
|
||||||
x += leftToRight ? buttonWidth : 0;
|
x += buttonWidth;
|
||||||
}
|
}
|
||||||
w -= buttonWidth;
|
w -= buttonWidth;
|
||||||
backwardButtonVisible = true;
|
backwardButtonVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// layout forward button on trailing side
|
|
||||||
if( !hideDisabledScrollButtons || viewSize.width - viewPosition.x > w ) {
|
|
||||||
int buttonWidth = forwardButton.getPreferredSize().width;
|
|
||||||
forwardButton.setBounds( leftToRight ? (x + w - buttonWidth) : x, y, buttonWidth, h );
|
|
||||||
x += leftToRight ? 0 : buttonWidth;
|
|
||||||
w -= buttonWidth;
|
|
||||||
forwardButtonVisible = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3916,6 +3939,8 @@ debug*/
|
|||||||
h -= buttonHeight;
|
h -= buttonHeight;
|
||||||
moreTabsButtonVisible = true;
|
moreTabsButtonVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// layout scroll buttons
|
||||||
if( useScrollButtons ) {
|
if( useScrollButtons ) {
|
||||||
// the tabViewport view size is set in
|
// the tabViewport view size is set in
|
||||||
// BasicTabbedPaneUI.TabbedPaneScrollLayout.calculateTabRects(),
|
// BasicTabbedPaneUI.TabbedPaneScrollLayout.calculateTabRects(),
|
||||||
@@ -3923,6 +3948,14 @@ debug*/
|
|||||||
Point viewPosition = tabViewport.getViewPosition();
|
Point viewPosition = tabViewport.getViewPosition();
|
||||||
Dimension viewSize = tabViewport.getViewSize();
|
Dimension viewSize = tabViewport.getViewSize();
|
||||||
|
|
||||||
|
// layout forward button on bottom side
|
||||||
|
if( !hideDisabledScrollButtons || viewSize.height - viewPosition.y > h ) {
|
||||||
|
int buttonHeight = forwardButton.getPreferredSize().height;
|
||||||
|
forwardButton.setBounds( x, y + h - buttonHeight, w, buttonHeight );
|
||||||
|
h -= buttonHeight;
|
||||||
|
forwardButtonVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
// layout backward button
|
// layout backward button
|
||||||
if( !hideDisabledScrollButtons || viewPosition.y > 0 ) {
|
if( !hideDisabledScrollButtons || viewPosition.y > 0 ) {
|
||||||
int buttonHeight = backwardButton.getPreferredSize().height;
|
int buttonHeight = backwardButton.getPreferredSize().height;
|
||||||
@@ -3937,14 +3970,6 @@ debug*/
|
|||||||
h -= buttonHeight;
|
h -= buttonHeight;
|
||||||
backwardButtonVisible = true;
|
backwardButtonVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// layout forward button on bottom side
|
|
||||||
if( !hideDisabledScrollButtons || viewSize.height - viewPosition.y > h ) {
|
|
||||||
int buttonHeight = forwardButton.getPreferredSize().height;
|
|
||||||
forwardButton.setBounds( x, y + h - buttonHeight, w, buttonHeight );
|
|
||||||
h -= buttonHeight;
|
|
||||||
forwardButtonVisible = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3985,10 +4010,8 @@ debug*/
|
|||||||
//---- class RunWithOriginalLayoutManagerDelegateAction -------------------
|
//---- class RunWithOriginalLayoutManagerDelegateAction -------------------
|
||||||
|
|
||||||
private static class RunWithOriginalLayoutManagerDelegateAction
|
private static class RunWithOriginalLayoutManagerDelegateAction
|
||||||
implements Action
|
extends FlatUIAction
|
||||||
{
|
{
|
||||||
private final Action delegate;
|
|
||||||
|
|
||||||
static void install( ActionMap map, String key ) {
|
static void install( ActionMap map, String key ) {
|
||||||
Action oldAction = map.get( key );
|
Action oldAction = map.get( key );
|
||||||
if( oldAction == null || oldAction instanceof RunWithOriginalLayoutManagerDelegateAction )
|
if( oldAction == null || oldAction instanceof RunWithOriginalLayoutManagerDelegateAction )
|
||||||
@@ -3998,24 +4021,9 @@ debug*/
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RunWithOriginalLayoutManagerDelegateAction( Action delegate ) {
|
private RunWithOriginalLayoutManagerDelegateAction( Action delegate ) {
|
||||||
this.delegate = delegate;
|
super( delegate );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getValue( String key ) {
|
|
||||||
return delegate.getValue( key );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return delegate.isEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void putValue( String key, Object value ) {}
|
|
||||||
@Override public void setEnabled( boolean b ) {}
|
|
||||||
@Override public void addPropertyChangeListener( PropertyChangeListener listener ) {}
|
|
||||||
@Override public void removePropertyChangeListener( PropertyChangeListener listener ) {}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed( ActionEvent e ) {
|
public void actionPerformed( ActionEvent e ) {
|
||||||
JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
|
JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -234,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
|
||||||
|
|||||||
@@ -24,31 +24,49 @@ 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.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.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.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.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicTableUI;
|
import javax.swing.plaf.basic.BasicTableUI;
|
||||||
import javax.swing.table.DefaultTableCellRenderer;
|
import javax.swing.table.DefaultTableCellRenderer;
|
||||||
import javax.swing.table.JTableHeader;
|
import javax.swing.table.JTableHeader;
|
||||||
import javax.swing.table.TableCellRenderer;
|
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.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;
|
||||||
@@ -88,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 -->
|
||||||
*
|
*
|
||||||
@@ -115,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;
|
||||||
@@ -127,6 +150,9 @@ public class FlatTableUI
|
|||||||
private TableCellRenderer oldBooleanRenderer;
|
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 ) {
|
||||||
@@ -153,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();
|
||||||
|
|
||||||
@@ -240,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;
|
||||||
@@ -248,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
|
||||||
@@ -261,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
|
||||||
@@ -284,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 {
|
||||||
@@ -325,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 );
|
||||||
}
|
}
|
||||||
@@ -387,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)
|
||||||
@@ -429,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -451,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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,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
|
||||||
@@ -506,10 +589,350 @@ 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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -579,4 +1002,36 @@ public class FlatTableUI
|
|||||||
selected = (value != null && (Boolean) 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;
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -338,6 +379,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 ) {
|
||||||
@@ -417,7 +465,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 +493,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 +518,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 +579,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 +595,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,
|
||||||
@@ -620,21 +685,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 )
|
g.setColor( Color.red );
|
||||||
paintRect( g, Color.red, r );
|
debugPaintComponentWithMouseListener( g, Color.red, rootPane.getLayeredPane(), 0, 0 );
|
||||||
}
|
|
||||||
paintRect( g, Color.cyan, debugCloseButtonBounds );
|
debugPaintRect( g, Color.blue, debugAppIconBounds );
|
||||||
paintRect( g, Color.blue, debugAppIconBounds );
|
debugPaintRect( g, Color.blue, debugMinimizeButtonBounds );
|
||||||
paintRect( g, Color.blue, debugMinimizeButtonBounds );
|
debugPaintRect( g, Color.magenta, debugMaximizeButtonBounds );
|
||||||
paintRect( g, Color.magenta, debugMaximizeButtonBounds );
|
debugPaintRect( g, Color.cyan, debugCloseButtonBounds );
|
||||||
paintRect( g, Color.cyan, debugCloseButtonBounds );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void paintRect( Graphics g, Color color, Rectangle r ) {
|
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;
|
||||||
|
|
||||||
@@ -645,6 +734,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)
|
||||||
@@ -653,16 +745,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.
|
||||||
*/
|
*/
|
||||||
@@ -846,6 +928,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 +952,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() ) {
|
||||||
@@ -928,71 +1017,17 @@ public class FlatTitlePane
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -1007,18 +1042,101 @@ 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:
|
||||||
|
* <ul>
|
||||||
|
* <li>This method is invoked often when mouse is moved over title bar
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
Point location = SwingUtilities.convertPoint( c, 0, 0, window );
|
// convert pt from window coordinates to layeredPane coordinates
|
||||||
Rectangle r = new Rectangle( location, size );
|
Component layeredPane = rootPane.getLayeredPane();
|
||||||
return r;
|
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() || !c.contains( 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -1041,7 +1159,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;
|
||||||
@@ -1060,7 +1178,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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1116,7 +1234,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 ) {
|
||||||
@@ -1125,7 +1243,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(),
|
||||||
@@ -1224,10 +1342,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
|
||||||
@@ -1235,10 +1350,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
|
||||||
@@ -1275,7 +1402,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() ) {
|
||||||
@@ -1302,7 +1429,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
|
||||||
@@ -1415,4 +1542,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,7 +132,7 @@ public class FlatToolBarSeparatorUI
|
|||||||
} else
|
} else
|
||||||
installStyle( s );
|
installStyle( s );
|
||||||
s.revalidate();
|
s.revalidate();
|
||||||
s.repaint();
|
HiDPIUtils.repaint( s );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -443,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 ) {
|
||||||
@@ -453,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 ------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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 );
|
||||||
@@ -304,6 +310,10 @@ public class FlatUIUtils
|
|||||||
if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c )
|
if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// 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
|
||||||
// Tree.cellEditor is set in sun.swing.FilePane.editFileName()
|
// Tree.cellEditor is set in sun.swing.FilePane.editFileName()
|
||||||
@@ -596,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;
|
||||||
@@ -775,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) -> {
|
||||||
@@ -1310,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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
return HTCLIENT;
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
// ignore
|
||||||
}
|
}
|
||||||
return isOnResizeBorder ? HTTOP : HTCAPTION;
|
|
||||||
|
// return HTCAPTION if mouse is over title bar
|
||||||
|
// - right-click shows system menu
|
||||||
|
// - double-left-click maximizes/restores window size
|
||||||
|
return HTCAPTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isOnResizeBorder ? HTTOP : HTCLIENT;
|
// return HTCLIENT
|
||||||
|
// - Windows ignores mouse events in this area
|
||||||
|
return HTCLIENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean contains( Rectangle rect, int x, int y ) {
|
private boolean contains( Rectangle rect, Point pt ) {
|
||||||
return (rect != null && rect.contains( x, y ) );
|
return (rect != null && rect.contains( pt ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
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;
|
||||||
@@ -25,6 +26,7 @@ import javax.swing.JList;
|
|||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.JTree;
|
import javax.swing.JTree;
|
||||||
|
import javax.swing.filechooser.FileSystemView;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
@@ -88,4 +90,64 @@ public class JavaCompatibility2
|
|||||||
return null;
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -739,6 +739,8 @@ 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.FlatScrollPaneBorder
|
Table.scrollPaneBorder = com.formdev.flatlaf.ui.FlatScrollPaneBorder
|
||||||
@@ -829,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
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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 ) );
|
||||||
@@ -799,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,
|
||||||
@@ -993,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,
|
||||||
|
|||||||
@@ -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 );
|
||||||
@@ -801,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 );
|
||||||
@@ -930,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 );
|
||||||
|
|
||||||
@@ -955,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 );
|
||||||
@@ -1025,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 );
|
||||||
|
|||||||
@@ -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" );
|
||||||
@@ -986,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" );
|
||||||
@@ -1294,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 );
|
||||||
|
|||||||
@@ -140,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(
|
||||||
@@ -153,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 -> {
|
||||||
|
|||||||
@@ -25,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.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,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() {
|
||||||
@@ -127,6 +131,28 @@ class DataComponentsPanel
|
|||||||
intercellSpacingCheckBox.setSelected( table1.getRowMargin() != 0 );
|
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" } )
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
||||||
@@ -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();
|
||||||
@@ -403,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");
|
||||||
|
|
||||||
@@ -477,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
|
||||||
|
|
||||||
|
|||||||
@@ -343,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"
|
||||||
@@ -354,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"
|
||||||
@@ -364,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"
|
||||||
@@ -374,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"
|
||||||
@@ -384,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"
|
||||||
@@ -395,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"
|
||||||
@@ -405,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"
|
||||||
@@ -416,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"
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ class DemoFrame
|
|||||||
initComponents();
|
initComponents();
|
||||||
updateFontMenuItems();
|
updateFontMenuItems();
|
||||||
initAccentColors();
|
initAccentColors();
|
||||||
|
initFullWindowContent();
|
||||||
controlBar.initialize( this, tabbedPane );
|
controlBar.initialize( this, tabbedPane );
|
||||||
|
|
||||||
setIconImages( FlatSVGUtils.createWindowIconImages( "/com/formdev/flatlaf/demo/FlatLaf.svg" ) );
|
setIconImages( FlatSVGUtils.createWindowIconImages( "/com/formdev/flatlaf/demo/FlatLaf.svg" ) );
|
||||||
@@ -89,24 +90,23 @@ class DemoFrame
|
|||||||
// do not use HTML text in menu items because this is not supported in macOS screen menu
|
// do not use HTML text in menu items because this is not supported in macOS screen menu
|
||||||
htmlMenuItem.setText( "some text" );
|
htmlMenuItem.setText( "some text" );
|
||||||
|
|
||||||
|
JRootPane rootPane = getRootPane();
|
||||||
if( SystemInfo.isMacFullWindowContentSupported ) {
|
if( SystemInfo.isMacFullWindowContentSupported ) {
|
||||||
// expand window content into window title bar and make title bar transparent
|
// expand window content into window title bar and make title bar transparent
|
||||||
getRootPane().putClientProperty( "apple.awt.fullWindowContent", true );
|
rootPane.putClientProperty( "apple.awt.fullWindowContent", true );
|
||||||
getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true );
|
rootPane.putClientProperty( "apple.awt.transparentTitleBar", true );
|
||||||
|
rootPane.putClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING, FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_LARGE );
|
||||||
|
|
||||||
// hide window title
|
// hide window title
|
||||||
if( SystemInfo.isJava_17_orLater )
|
if( SystemInfo.isJava_17_orLater )
|
||||||
getRootPane().putClientProperty( "apple.awt.windowTitleVisible", false );
|
rootPane.putClientProperty( "apple.awt.windowTitleVisible", false );
|
||||||
else
|
else
|
||||||
setTitle( null );
|
setTitle( null );
|
||||||
|
|
||||||
// add gap to left side of toolbar
|
|
||||||
toolBar.add( Box.createHorizontalStrut( 70 ), 0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable full screen mode for this window (for Java 8 - 10; not necessary for Java 11+)
|
// enable full screen mode for this window (for Java 8 - 10; not necessary for Java 11+)
|
||||||
if( !SystemInfo.isJava_11_orLater )
|
if( !SystemInfo.isJava_11_orLater )
|
||||||
getRootPane().putClientProperty( "apple.awt.fullscreenable", true );
|
rootPane.putClientProperty( "apple.awt.fullscreenable", true );
|
||||||
}
|
}
|
||||||
|
|
||||||
// integrate into macOS screen menu
|
// integrate into macOS screen menu
|
||||||
@@ -461,9 +461,37 @@ class DemoFrame
|
|||||||
accentColorButtons[i].setVisible( isAccentColorSupported );
|
accentColorButtons[i].setVisible( isAccentColorSupported );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initFullWindowContent() {
|
||||||
|
if( !supportsFlatLafWindowDecorations() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// create fullWindowContent mode toggle button
|
||||||
|
Icon expandIcon = new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/expand.svg" );
|
||||||
|
Icon collapseIcon = new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/collapse.svg" );
|
||||||
|
JToggleButton fullWindowContentButton = new JToggleButton( expandIcon );
|
||||||
|
fullWindowContentButton.setToolTipText( "Toggle full window content" );
|
||||||
|
fullWindowContentButton.addActionListener( e -> {
|
||||||
|
boolean fullWindowContent = fullWindowContentButton.isSelected();
|
||||||
|
fullWindowContentButton.setIcon( fullWindowContent ? collapseIcon : expandIcon );
|
||||||
|
menuBar.setVisible( !fullWindowContent );
|
||||||
|
toolBar.setVisible( !fullWindowContent );
|
||||||
|
getRootPane().putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT, fullWindowContent );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// add fullWindowContent mode toggle button to tabbed pane
|
||||||
|
JToolBar trailingToolBar = new JToolBar();
|
||||||
|
trailingToolBar.add( Box.createGlue() );
|
||||||
|
trailingToolBar.add( fullWindowContentButton );
|
||||||
|
tabbedPane.putClientProperty( FlatClientProperties.TABBED_PANE_TRAILING_COMPONENT, trailingToolBar );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean supportsFlatLafWindowDecorations() {
|
||||||
|
return FlatLaf.supportsNativeWindowDecorations() || (SystemInfo.isLinux && JFrame.isDefaultLookAndFeelDecorated());
|
||||||
|
}
|
||||||
|
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
||||||
JMenuBar menuBar1 = new JMenuBar();
|
menuBar = new JMenuBar();
|
||||||
JMenu fileMenu = new JMenu();
|
JMenu fileMenu = new JMenu();
|
||||||
JMenuItem newMenuItem = new JMenuItem();
|
JMenuItem newMenuItem = new JMenuItem();
|
||||||
JMenuItem openMenuItem = new JMenuItem();
|
JMenuItem openMenuItem = new JMenuItem();
|
||||||
@@ -509,6 +537,8 @@ class DemoFrame
|
|||||||
JMenuItem showUIDefaultsInspectorMenuItem = new JMenuItem();
|
JMenuItem showUIDefaultsInspectorMenuItem = new JMenuItem();
|
||||||
JMenu helpMenu = new JMenu();
|
JMenu helpMenu = new JMenu();
|
||||||
aboutMenuItem = new JMenuItem();
|
aboutMenuItem = new JMenuItem();
|
||||||
|
JPanel toolBarPanel = new JPanel();
|
||||||
|
JPanel macFullWindowContentButtonsPlaceholder = new JPanel();
|
||||||
toolBar = new JToolBar();
|
toolBar = new JToolBar();
|
||||||
JButton backButton = new JButton();
|
JButton backButton = new JButton();
|
||||||
JButton forwardButton = new JButton();
|
JButton forwardButton = new JButton();
|
||||||
@@ -524,8 +554,10 @@ class DemoFrame
|
|||||||
DataComponentsPanel dataComponentsPanel = new DataComponentsPanel();
|
DataComponentsPanel dataComponentsPanel = new DataComponentsPanel();
|
||||||
TabsPanel tabsPanel = new TabsPanel();
|
TabsPanel tabsPanel = new TabsPanel();
|
||||||
OptionPanePanel optionPanePanel = new OptionPanePanel();
|
OptionPanePanel optionPanePanel = new OptionPanePanel();
|
||||||
ExtrasPanel extrasPanel1 = new ExtrasPanel();
|
ExtrasPanel extrasPanel = new ExtrasPanel();
|
||||||
controlBar = new ControlBar();
|
controlBar = new ControlBar();
|
||||||
|
JPanel themesPanelPanel = new JPanel();
|
||||||
|
JPanel winFullWindowContentButtonsPlaceholder = new JPanel();
|
||||||
themesPanel = new IJThemesPanel();
|
themesPanel = new IJThemesPanel();
|
||||||
|
|
||||||
//======== this ========
|
//======== this ========
|
||||||
@@ -534,7 +566,7 @@ class DemoFrame
|
|||||||
Container contentPane = getContentPane();
|
Container contentPane = getContentPane();
|
||||||
contentPane.setLayout(new BorderLayout());
|
contentPane.setLayout(new BorderLayout());
|
||||||
|
|
||||||
//======== menuBar1 ========
|
//======== menuBar ========
|
||||||
{
|
{
|
||||||
|
|
||||||
//======== fileMenu ========
|
//======== fileMenu ========
|
||||||
@@ -579,7 +611,7 @@ class DemoFrame
|
|||||||
exitMenuItem.addActionListener(e -> exitActionPerformed());
|
exitMenuItem.addActionListener(e -> exitActionPerformed());
|
||||||
fileMenu.add(exitMenuItem);
|
fileMenu.add(exitMenuItem);
|
||||||
}
|
}
|
||||||
menuBar1.add(fileMenu);
|
menuBar.add(fileMenu);
|
||||||
|
|
||||||
//======== editMenu ========
|
//======== editMenu ========
|
||||||
{
|
{
|
||||||
@@ -632,7 +664,7 @@ class DemoFrame
|
|||||||
deleteMenuItem.addActionListener(e -> menuItemActionPerformed(e));
|
deleteMenuItem.addActionListener(e -> menuItemActionPerformed(e));
|
||||||
editMenu.add(deleteMenuItem);
|
editMenu.add(deleteMenuItem);
|
||||||
}
|
}
|
||||||
menuBar1.add(editMenu);
|
menuBar.add(editMenu);
|
||||||
|
|
||||||
//======== viewMenu ========
|
//======== viewMenu ========
|
||||||
{
|
{
|
||||||
@@ -732,7 +764,7 @@ class DemoFrame
|
|||||||
radioButtonMenuItem3.addActionListener(e -> menuItemActionPerformed(e));
|
radioButtonMenuItem3.addActionListener(e -> menuItemActionPerformed(e));
|
||||||
viewMenu.add(radioButtonMenuItem3);
|
viewMenu.add(radioButtonMenuItem3);
|
||||||
}
|
}
|
||||||
menuBar1.add(viewMenu);
|
menuBar.add(viewMenu);
|
||||||
|
|
||||||
//======== fontMenu ========
|
//======== fontMenu ========
|
||||||
{
|
{
|
||||||
@@ -756,7 +788,7 @@ class DemoFrame
|
|||||||
decrFontMenuItem.addActionListener(e -> decrFont());
|
decrFontMenuItem.addActionListener(e -> decrFont());
|
||||||
fontMenu.add(decrFontMenuItem);
|
fontMenu.add(decrFontMenuItem);
|
||||||
}
|
}
|
||||||
menuBar1.add(fontMenu);
|
menuBar.add(fontMenu);
|
||||||
|
|
||||||
//======== optionsMenu ========
|
//======== optionsMenu ========
|
||||||
{
|
{
|
||||||
@@ -808,7 +840,7 @@ class DemoFrame
|
|||||||
showUIDefaultsInspectorMenuItem.addActionListener(e -> showUIDefaultsInspector());
|
showUIDefaultsInspectorMenuItem.addActionListener(e -> showUIDefaultsInspector());
|
||||||
optionsMenu.add(showUIDefaultsInspectorMenuItem);
|
optionsMenu.add(showUIDefaultsInspectorMenuItem);
|
||||||
}
|
}
|
||||||
menuBar1.add(optionsMenu);
|
menuBar.add(optionsMenu);
|
||||||
|
|
||||||
//======== helpMenu ========
|
//======== helpMenu ========
|
||||||
{
|
{
|
||||||
@@ -821,54 +853,66 @@ class DemoFrame
|
|||||||
aboutMenuItem.addActionListener(e -> aboutActionPerformed());
|
aboutMenuItem.addActionListener(e -> aboutActionPerformed());
|
||||||
helpMenu.add(aboutMenuItem);
|
helpMenu.add(aboutMenuItem);
|
||||||
}
|
}
|
||||||
menuBar1.add(helpMenu);
|
menuBar.add(helpMenu);
|
||||||
}
|
}
|
||||||
setJMenuBar(menuBar1);
|
setJMenuBar(menuBar);
|
||||||
|
|
||||||
//======== toolBar ========
|
//======== toolBarPanel ========
|
||||||
{
|
{
|
||||||
toolBar.setMargin(new Insets(3, 3, 3, 3));
|
toolBarPanel.setLayout(new BorderLayout());
|
||||||
|
|
||||||
//---- backButton ----
|
//======== macFullWindowContentButtonsPlaceholder ========
|
||||||
backButton.setToolTipText("Back");
|
{
|
||||||
backButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/back.svg"));
|
macFullWindowContentButtonsPlaceholder.setLayout(new FlowLayout());
|
||||||
toolBar.add(backButton);
|
}
|
||||||
|
toolBarPanel.add(macFullWindowContentButtonsPlaceholder, BorderLayout.WEST);
|
||||||
|
|
||||||
//---- forwardButton ----
|
//======== toolBar ========
|
||||||
forwardButton.setToolTipText("Forward");
|
{
|
||||||
forwardButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/forward.svg"));
|
toolBar.setMargin(new Insets(3, 3, 3, 3));
|
||||||
toolBar.add(forwardButton);
|
|
||||||
toolBar.addSeparator();
|
|
||||||
|
|
||||||
//---- cutButton ----
|
//---- backButton ----
|
||||||
cutButton.setToolTipText("Cut");
|
backButton.setToolTipText("Back");
|
||||||
cutButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-cut.svg"));
|
backButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/back.svg"));
|
||||||
toolBar.add(cutButton);
|
toolBar.add(backButton);
|
||||||
|
|
||||||
//---- copyButton ----
|
//---- forwardButton ----
|
||||||
copyButton.setToolTipText("Copy");
|
forwardButton.setToolTipText("Forward");
|
||||||
copyButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/copy.svg"));
|
forwardButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/forward.svg"));
|
||||||
toolBar.add(copyButton);
|
toolBar.add(forwardButton);
|
||||||
|
toolBar.addSeparator();
|
||||||
|
|
||||||
//---- pasteButton ----
|
//---- cutButton ----
|
||||||
pasteButton.setToolTipText("Paste");
|
cutButton.setToolTipText("Cut");
|
||||||
pasteButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-paste.svg"));
|
cutButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-cut.svg"));
|
||||||
toolBar.add(pasteButton);
|
toolBar.add(cutButton);
|
||||||
toolBar.addSeparator();
|
|
||||||
|
|
||||||
//---- refreshButton ----
|
//---- copyButton ----
|
||||||
refreshButton.setToolTipText("Refresh");
|
copyButton.setToolTipText("Copy");
|
||||||
refreshButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/refresh.svg"));
|
copyButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/copy.svg"));
|
||||||
toolBar.add(refreshButton);
|
toolBar.add(copyButton);
|
||||||
toolBar.addSeparator();
|
|
||||||
|
|
||||||
//---- showToggleButton ----
|
//---- pasteButton ----
|
||||||
showToggleButton.setSelected(true);
|
pasteButton.setToolTipText("Paste");
|
||||||
showToggleButton.setToolTipText("Show Details");
|
pasteButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-paste.svg"));
|
||||||
showToggleButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/show.svg"));
|
toolBar.add(pasteButton);
|
||||||
toolBar.add(showToggleButton);
|
toolBar.addSeparator();
|
||||||
|
|
||||||
|
//---- refreshButton ----
|
||||||
|
refreshButton.setToolTipText("Refresh");
|
||||||
|
refreshButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/refresh.svg"));
|
||||||
|
toolBar.add(refreshButton);
|
||||||
|
toolBar.addSeparator();
|
||||||
|
|
||||||
|
//---- showToggleButton ----
|
||||||
|
showToggleButton.setSelected(true);
|
||||||
|
showToggleButton.setToolTipText("Show Details");
|
||||||
|
showToggleButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/show.svg"));
|
||||||
|
toolBar.add(showToggleButton);
|
||||||
|
}
|
||||||
|
toolBarPanel.add(toolBar, BorderLayout.CENTER);
|
||||||
}
|
}
|
||||||
contentPane.add(toolBar, BorderLayout.NORTH);
|
contentPane.add(toolBarPanel, BorderLayout.PAGE_START);
|
||||||
|
|
||||||
//======== contentPanel ========
|
//======== contentPanel ========
|
||||||
{
|
{
|
||||||
@@ -888,13 +932,25 @@ class DemoFrame
|
|||||||
tabbedPane.addTab("Data Components", dataComponentsPanel);
|
tabbedPane.addTab("Data Components", dataComponentsPanel);
|
||||||
tabbedPane.addTab("Tabs", tabsPanel);
|
tabbedPane.addTab("Tabs", tabsPanel);
|
||||||
tabbedPane.addTab("Option Pane", optionPanePanel);
|
tabbedPane.addTab("Option Pane", optionPanePanel);
|
||||||
tabbedPane.addTab("Extras", extrasPanel1);
|
tabbedPane.addTab("Extras", extrasPanel);
|
||||||
}
|
}
|
||||||
contentPanel.add(tabbedPane, "cell 0 0");
|
contentPanel.add(tabbedPane, "cell 0 0");
|
||||||
}
|
}
|
||||||
contentPane.add(contentPanel, BorderLayout.CENTER);
|
contentPane.add(contentPanel, BorderLayout.CENTER);
|
||||||
contentPane.add(controlBar, BorderLayout.SOUTH);
|
contentPane.add(controlBar, BorderLayout.PAGE_END);
|
||||||
contentPane.add(themesPanel, BorderLayout.EAST);
|
|
||||||
|
//======== themesPanelPanel ========
|
||||||
|
{
|
||||||
|
themesPanelPanel.setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
//======== winFullWindowContentButtonsPlaceholder ========
|
||||||
|
{
|
||||||
|
winFullWindowContentButtonsPlaceholder.setLayout(new FlowLayout());
|
||||||
|
}
|
||||||
|
themesPanelPanel.add(winFullWindowContentButtonsPlaceholder, BorderLayout.NORTH);
|
||||||
|
themesPanelPanel.add(themesPanel, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
contentPane.add(themesPanelPanel, BorderLayout.LINE_END);
|
||||||
|
|
||||||
//---- buttonGroup1 ----
|
//---- buttonGroup1 ----
|
||||||
ButtonGroup buttonGroup1 = new ButtonGroup();
|
ButtonGroup buttonGroup1 = new ButtonGroup();
|
||||||
@@ -909,8 +965,8 @@ class DemoFrame
|
|||||||
usersButton.setButtonType( ButtonType.toolBarButton );
|
usersButton.setButtonType( ButtonType.toolBarButton );
|
||||||
usersButton.setFocusable( false );
|
usersButton.setFocusable( false );
|
||||||
usersButton.addActionListener( e -> JOptionPane.showMessageDialog( null, "Hello User! How are you?", "User", JOptionPane.INFORMATION_MESSAGE ) );
|
usersButton.addActionListener( e -> JOptionPane.showMessageDialog( null, "Hello User! How are you?", "User", JOptionPane.INFORMATION_MESSAGE ) );
|
||||||
menuBar1.add( Box.createGlue() );
|
menuBar.add( Box.createGlue() );
|
||||||
menuBar1.add( usersButton );
|
menuBar.add( usersButton );
|
||||||
|
|
||||||
cutMenuItem.addActionListener( new DefaultEditorKit.CutAction() );
|
cutMenuItem.addActionListener( new DefaultEditorKit.CutAction() );
|
||||||
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
|
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
|
||||||
@@ -922,7 +978,7 @@ class DemoFrame
|
|||||||
for( int i = 1; i <= 100; i++ )
|
for( int i = 1; i <= 100; i++ )
|
||||||
scrollingPopupMenu.add( "Item " + i );
|
scrollingPopupMenu.add( "Item " + i );
|
||||||
|
|
||||||
if( FlatLaf.supportsNativeWindowDecorations() || (SystemInfo.isLinux && JFrame.isDefaultLookAndFeelDecorated()) ) {
|
if( supportsFlatLafWindowDecorations() ) {
|
||||||
if( SystemInfo.isLinux )
|
if( SystemInfo.isLinux )
|
||||||
unsupported( windowDecorationsCheckBoxMenuItem );
|
unsupported( windowDecorationsCheckBoxMenuItem );
|
||||||
else
|
else
|
||||||
@@ -943,6 +999,17 @@ class DemoFrame
|
|||||||
if( "false".equals( System.getProperty( "flatlaf.animatedLafChange" ) ) )
|
if( "false".equals( System.getProperty( "flatlaf.animatedLafChange" ) ) )
|
||||||
animatedLafChangeMenuItem.setSelected( false );
|
animatedLafChangeMenuItem.setSelected( false );
|
||||||
|
|
||||||
|
|
||||||
|
// on macOS, panel left to toolBar is a placeholder for title bar buttons in fullWindowContent mode
|
||||||
|
macFullWindowContentButtonsPlaceholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac zeroInFullScreen" );
|
||||||
|
|
||||||
|
// on Windows/Linux, panel above themesPanel is a placeholder for title bar buttons in fullWindowContent mode
|
||||||
|
winFullWindowContentButtonsPlaceholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "win" );
|
||||||
|
|
||||||
|
// uncomment this line to see title bar buttons placeholders in fullWindowContent mode
|
||||||
|
// UIManager.put( "FlatLaf.debug.panel.showPlaceholders", true );
|
||||||
|
|
||||||
|
|
||||||
// remove contentPanel bottom insets
|
// remove contentPanel bottom insets
|
||||||
MigLayout layout = (MigLayout) contentPanel.getLayout();
|
MigLayout layout = (MigLayout) contentPanel.getLayout();
|
||||||
LC lc = ConstraintParser.parseLayoutConstraint( (String) layout.getLayoutConstraints() );
|
LC lc = ConstraintParser.parseLayoutConstraint( (String) layout.getLayoutConstraints() );
|
||||||
@@ -963,6 +1030,7 @@ class DemoFrame
|
|||||||
}
|
}
|
||||||
|
|
||||||
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
|
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
|
||||||
|
private JMenuBar menuBar;
|
||||||
private JMenuItem exitMenuItem;
|
private JMenuItem exitMenuItem;
|
||||||
private JMenu scrollingPopupMenu;
|
private JMenu scrollingPopupMenu;
|
||||||
private JMenuItem htmlMenuItem;
|
private JMenuItem htmlMenuItem;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
JFDML JFormDesigner: "8.1.0.0.283" Java: "19.0.2" encoding: "UTF-8"
|
JFDML JFormDesigner: "8.2.1.0.348" Java: "21.0.1" encoding: "UTF-8"
|
||||||
|
|
||||||
new FormModel {
|
new FormModel {
|
||||||
contentType: "form/swing"
|
contentType: "form/swing"
|
||||||
@@ -12,59 +12,69 @@ new FormModel {
|
|||||||
"defaultCloseOperation": 2
|
"defaultCloseOperation": 2
|
||||||
"$locationPolicy": 2
|
"$locationPolicy": 2
|
||||||
"$sizePolicy": 2
|
"$sizePolicy": 2
|
||||||
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
|
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
|
||||||
name: "toolBar"
|
name: "toolBarPanel"
|
||||||
"margin": new java.awt.Insets( 3, 3, 3, 3 )
|
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
|
||||||
auxiliary() {
|
name: "macFullWindowContentButtonsPlaceholder"
|
||||||
"JavaCodeGenerator.variableLocal": false
|
}, new FormLayoutConstraints( class java.lang.String ) {
|
||||||
}
|
"value": "West"
|
||||||
add( new FormComponent( "javax.swing.JButton" ) {
|
|
||||||
name: "backButton"
|
|
||||||
"toolTipText": "Back"
|
|
||||||
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/back.svg" )
|
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JButton" ) {
|
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
|
||||||
name: "forwardButton"
|
name: "toolBar"
|
||||||
"toolTipText": "Forward"
|
"margin": new java.awt.Insets( 3, 3, 3, 3 )
|
||||||
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/forward.svg" )
|
auxiliary() {
|
||||||
} )
|
"JavaCodeGenerator.variableLocal": false
|
||||||
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
|
}
|
||||||
name: "separator5"
|
add( new FormComponent( "javax.swing.JButton" ) {
|
||||||
} )
|
name: "backButton"
|
||||||
add( new FormComponent( "javax.swing.JButton" ) {
|
"toolTipText": "Back"
|
||||||
name: "cutButton"
|
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/back.svg" )
|
||||||
"toolTipText": "Cut"
|
} )
|
||||||
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-cut.svg" )
|
add( new FormComponent( "javax.swing.JButton" ) {
|
||||||
} )
|
name: "forwardButton"
|
||||||
add( new FormComponent( "javax.swing.JButton" ) {
|
"toolTipText": "Forward"
|
||||||
name: "copyButton"
|
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/forward.svg" )
|
||||||
"toolTipText": "Copy"
|
} )
|
||||||
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/copy.svg" )
|
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
|
||||||
} )
|
name: "separator5"
|
||||||
add( new FormComponent( "javax.swing.JButton" ) {
|
} )
|
||||||
name: "pasteButton"
|
add( new FormComponent( "javax.swing.JButton" ) {
|
||||||
"toolTipText": "Paste"
|
name: "cutButton"
|
||||||
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-paste.svg" )
|
"toolTipText": "Cut"
|
||||||
} )
|
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-cut.svg" )
|
||||||
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
|
} )
|
||||||
name: "separator6"
|
add( new FormComponent( "javax.swing.JButton" ) {
|
||||||
} )
|
name: "copyButton"
|
||||||
add( new FormComponent( "javax.swing.JButton" ) {
|
"toolTipText": "Copy"
|
||||||
name: "refreshButton"
|
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/copy.svg" )
|
||||||
"toolTipText": "Refresh"
|
} )
|
||||||
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/refresh.svg" )
|
add( new FormComponent( "javax.swing.JButton" ) {
|
||||||
} )
|
name: "pasteButton"
|
||||||
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
|
"toolTipText": "Paste"
|
||||||
name: "separator7"
|
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-paste.svg" )
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "javax.swing.JToggleButton" ) {
|
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
|
||||||
name: "showToggleButton"
|
name: "separator6"
|
||||||
"selected": true
|
} )
|
||||||
"toolTipText": "Show Details"
|
add( new FormComponent( "javax.swing.JButton" ) {
|
||||||
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/show.svg" )
|
name: "refreshButton"
|
||||||
|
"toolTipText": "Refresh"
|
||||||
|
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/refresh.svg" )
|
||||||
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
|
||||||
|
name: "separator7"
|
||||||
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JToggleButton" ) {
|
||||||
|
name: "showToggleButton"
|
||||||
|
"selected": true
|
||||||
|
"toolTipText": "Show Details"
|
||||||
|
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/show.svg" )
|
||||||
|
} )
|
||||||
|
}, new FormLayoutConstraints( class java.lang.String ) {
|
||||||
|
"value": "Center"
|
||||||
} )
|
} )
|
||||||
}, new FormLayoutConstraints( class java.lang.String ) {
|
}, new FormLayoutConstraints( class java.lang.String ) {
|
||||||
"value": "North"
|
"value": "First"
|
||||||
} )
|
} )
|
||||||
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 dialog,hidemode 3"
|
"$layoutConstraints": "insets dialog,hidemode 3"
|
||||||
@@ -105,7 +115,7 @@ new FormModel {
|
|||||||
"title": "Option Pane"
|
"title": "Option Pane"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "com.formdev.flatlaf.demo.extras.ExtrasPanel" ) {
|
add( new FormComponent( "com.formdev.flatlaf.demo.extras.ExtrasPanel" ) {
|
||||||
name: "extrasPanel1"
|
name: "extrasPanel"
|
||||||
}, new FormLayoutConstraints( null ) {
|
}, new FormLayoutConstraints( null ) {
|
||||||
"title": "Extras"
|
"title": "Extras"
|
||||||
} )
|
} )
|
||||||
@@ -121,19 +131,32 @@ new FormModel {
|
|||||||
"JavaCodeGenerator.variableLocal": false
|
"JavaCodeGenerator.variableLocal": false
|
||||||
}
|
}
|
||||||
}, new FormLayoutConstraints( class java.lang.String ) {
|
}, new FormLayoutConstraints( class java.lang.String ) {
|
||||||
"value": "South"
|
"value": "Last"
|
||||||
} )
|
} )
|
||||||
add( new FormComponent( "com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel" ) {
|
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
|
||||||
name: "themesPanel"
|
name: "themesPanelPanel"
|
||||||
auxiliary() {
|
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
|
||||||
"JavaCodeGenerator.variableLocal": false
|
name: "winFullWindowContentButtonsPlaceholder"
|
||||||
"JavaCodeGenerator.variableModifiers": 0
|
}, new FormLayoutConstraints( class java.lang.String ) {
|
||||||
}
|
"value": "North"
|
||||||
|
} )
|
||||||
|
add( new FormComponent( "com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel" ) {
|
||||||
|
name: "themesPanel"
|
||||||
|
auxiliary() {
|
||||||
|
"JavaCodeGenerator.variableLocal": false
|
||||||
|
"JavaCodeGenerator.variableModifiers": 0
|
||||||
|
}
|
||||||
|
}, new FormLayoutConstraints( class java.lang.String ) {
|
||||||
|
"value": "Center"
|
||||||
|
} )
|
||||||
}, new FormLayoutConstraints( class java.lang.String ) {
|
}, new FormLayoutConstraints( class java.lang.String ) {
|
||||||
"value": "East"
|
"value": "After"
|
||||||
} )
|
} )
|
||||||
menuBar: new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) {
|
menuBar: new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) {
|
||||||
name: "menuBar1"
|
name: "menuBar"
|
||||||
|
auxiliary() {
|
||||||
|
"JavaCodeGenerator.variableLocal": false
|
||||||
|
}
|
||||||
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
|
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
|
||||||
name: "fileMenu"
|
name: "fileMenu"
|
||||||
"text": "File"
|
"text": "File"
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ package com.formdev.flatlaf.demo;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.util.prefs.Preferences;
|
import java.util.prefs.Preferences;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.FlatLightLaf;
|
import com.formdev.flatlaf.FlatLightLaf;
|
||||||
@@ -27,6 +31,7 @@ import com.formdev.flatlaf.IntelliJTheme;
|
|||||||
import com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel;
|
import com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -35,6 +40,7 @@ public class DemoPrefs
|
|||||||
{
|
{
|
||||||
public static final String KEY_LAF = "laf";
|
public static final String KEY_LAF = "laf";
|
||||||
public static final String KEY_LAF_THEME = "lafTheme";
|
public static final String KEY_LAF_THEME = "lafTheme";
|
||||||
|
public static final String KEY_SYSTEM_SCALE_FACTOR = "systemScaleFactor";
|
||||||
|
|
||||||
public static final String RESOURCE_PREFIX = "res:";
|
public static final String RESOURCE_PREFIX = "res:";
|
||||||
public static final String FILE_PREFIX = "file:";
|
public static final String FILE_PREFIX = "file:";
|
||||||
@@ -96,4 +102,66 @@ public class DemoPrefs
|
|||||||
state.put( KEY_LAF, UIManager.getLookAndFeel().getClass().getName() );
|
state.put( KEY_LAF, UIManager.getLookAndFeel().getClass().getName() );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void initSystemScale() {
|
||||||
|
if( System.getProperty( "sun.java2d.uiScale" ) == null ) {
|
||||||
|
String scaleFactor = getState().get( KEY_SYSTEM_SCALE_FACTOR, null );
|
||||||
|
if( scaleFactor != null )
|
||||||
|
System.setProperty( "sun.java2d.uiScale", scaleFactor );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* register Alt+Shift+F1, F2, ... F12 keys to change system scale factor
|
||||||
|
*/
|
||||||
|
public static void registerSystemScaleFactors( JFrame frame ) {
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F1", null );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F2", "1" );
|
||||||
|
|
||||||
|
if( SystemInfo.isWindows ) {
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F3", "1.25" );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F4", "1.5" );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F5", "1.75" );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F6", "2" );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F7", "2.25" );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F8", "2.5" );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F9", "2.75" );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F10", "3" );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F11", "3.5" );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F12", "4" );
|
||||||
|
} else {
|
||||||
|
// Java on macOS and Linux supports only integer scale factors
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F3", "2" );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F4", "3" );
|
||||||
|
registerSystemScaleFactor( frame, "alt shift F5", "4" );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerSystemScaleFactor( JFrame frame, String keyStrokeStr, String scaleFactor ) {
|
||||||
|
KeyStroke keyStroke = KeyStroke.getKeyStroke( keyStrokeStr );
|
||||||
|
if( keyStroke == null )
|
||||||
|
throw new IllegalArgumentException( "Invalid key stroke '" + keyStrokeStr + "'" );
|
||||||
|
|
||||||
|
((JComponent)frame.getContentPane()).registerKeyboardAction(
|
||||||
|
e -> applySystemScaleFactor( frame, scaleFactor ),
|
||||||
|
keyStroke,
|
||||||
|
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applySystemScaleFactor( JFrame frame, String scaleFactor ) {
|
||||||
|
if( JOptionPane.showConfirmDialog( frame,
|
||||||
|
"Change system scale factor to "
|
||||||
|
+ (scaleFactor != null ? scaleFactor : "default")
|
||||||
|
+ " and exit?",
|
||||||
|
frame.getTitle(), JOptionPane.YES_NO_OPTION ) != JOptionPane.YES_OPTION )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( scaleFactor != null )
|
||||||
|
DemoPrefs.getState().put( KEY_SYSTEM_SCALE_FACTOR, scaleFactor );
|
||||||
|
else
|
||||||
|
DemoPrefs.getState().remove( KEY_SYSTEM_SCALE_FACTOR );
|
||||||
|
|
||||||
|
System.exit( 0 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,9 +70,10 @@ public class FlatLafDemo
|
|||||||
if( FlatLafDemo.screenshotsMode && !SystemInfo.isJava_9_orLater && System.getProperty( "flatlaf.uiScale" ) == null )
|
if( FlatLafDemo.screenshotsMode && !SystemInfo.isJava_9_orLater && System.getProperty( "flatlaf.uiScale" ) == null )
|
||||||
System.setProperty( "flatlaf.uiScale", "2x" );
|
System.setProperty( "flatlaf.uiScale", "2x" );
|
||||||
|
|
||||||
SwingUtilities.invokeLater( () -> {
|
DemoPrefs.init( PREFS_ROOT_PATH );
|
||||||
DemoPrefs.init( PREFS_ROOT_PATH );
|
DemoPrefs.initSystemScale();
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater( () -> {
|
||||||
// install fonts for lazy loading
|
// install fonts for lazy loading
|
||||||
FlatInterFont.installLazy();
|
FlatInterFont.installLazy();
|
||||||
FlatJetBrainsMonoFont.installLazy();
|
FlatJetBrainsMonoFont.installLazy();
|
||||||
@@ -95,6 +96,9 @@ public class FlatLafDemo
|
|||||||
// use Roboto Mono font
|
// use Roboto Mono font
|
||||||
// FlatLaf.setPreferredMonospacedFontFamily( FlatRobotoMonoFont.FAMILY );
|
// FlatLaf.setPreferredMonospacedFontFamily( FlatRobotoMonoFont.FAMILY );
|
||||||
|
|
||||||
|
// install own repaint manager to fix repaint issues at 125%, 175%, 225%, ... on Windows
|
||||||
|
// HiDPIUtils.installHiDPIRepaintManager();
|
||||||
|
|
||||||
// application specific UI defaults
|
// application specific UI defaults
|
||||||
FlatLaf.registerCustomDefaultsSource( "com.formdev.flatlaf.demo" );
|
FlatLaf.registerCustomDefaultsSource( "com.formdev.flatlaf.demo" );
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,14 @@ class MoreComponentsPanel
|
|||||||
JLabel label5 = new JLabel();
|
JLabel label5 = new JLabel();
|
||||||
JPanel panel13 = new JPanel();
|
JPanel panel13 = new JPanel();
|
||||||
JLabel label6 = new JLabel();
|
JLabel label6 = new JLabel();
|
||||||
|
JLabel panelLabel = new JLabel();
|
||||||
|
JPanel panel5 = new JPanel();
|
||||||
|
JLabel label9 = new JLabel();
|
||||||
|
JPanel panel4 = new JPanel();
|
||||||
|
JLabel label8 = new JLabel();
|
||||||
|
JLabel labelLabel = new JLabel();
|
||||||
|
JLabel label13 = new JLabel();
|
||||||
|
JLabel label10 = new JLabel();
|
||||||
|
|
||||||
//======== this ========
|
//======== this ========
|
||||||
setLayout(new MigLayout(
|
setLayout(new MigLayout(
|
||||||
@@ -140,7 +148,9 @@ class MoreComponentsPanel
|
|||||||
"[]" +
|
"[]" +
|
||||||
"[]" +
|
"[]" +
|
||||||
"[]" +
|
"[]" +
|
||||||
"[100,top]"));
|
"[100,top]" +
|
||||||
|
"[50,top]" +
|
||||||
|
"[]"));
|
||||||
|
|
||||||
//---- scrollPaneLabel ----
|
//---- scrollPaneLabel ----
|
||||||
scrollPaneLabel.setText("JScrollPane:");
|
scrollPaneLabel.setText("JScrollPane:");
|
||||||
@@ -441,7 +451,7 @@ class MoreComponentsPanel
|
|||||||
|
|
||||||
//======== panel10 ========
|
//======== panel10 ========
|
||||||
{
|
{
|
||||||
panel10.setBackground(new Color(217, 163, 67));
|
panel10.setBackground(new Color(0xd9a343));
|
||||||
panel10.setLayout(new BorderLayout());
|
panel10.setLayout(new BorderLayout());
|
||||||
|
|
||||||
//---- label1 ----
|
//---- label1 ----
|
||||||
@@ -454,7 +464,7 @@ class MoreComponentsPanel
|
|||||||
|
|
||||||
//======== panel11 ========
|
//======== panel11 ========
|
||||||
{
|
{
|
||||||
panel11.setBackground(new Color(98, 181, 67));
|
panel11.setBackground(new Color(0x62b543));
|
||||||
panel11.setLayout(new BorderLayout());
|
panel11.setLayout(new BorderLayout());
|
||||||
|
|
||||||
//---- label2 ----
|
//---- label2 ----
|
||||||
@@ -474,7 +484,7 @@ class MoreComponentsPanel
|
|||||||
|
|
||||||
//======== panel12 ========
|
//======== panel12 ========
|
||||||
{
|
{
|
||||||
panel12.setBackground(new Color(242, 101, 34));
|
panel12.setBackground(new Color(0xf26522));
|
||||||
panel12.setLayout(new BorderLayout());
|
panel12.setLayout(new BorderLayout());
|
||||||
|
|
||||||
//---- label5 ----
|
//---- label5 ----
|
||||||
@@ -487,7 +497,7 @@ class MoreComponentsPanel
|
|||||||
|
|
||||||
//======== panel13 ========
|
//======== panel13 ========
|
||||||
{
|
{
|
||||||
panel13.setBackground(new Color(64, 182, 224));
|
panel13.setBackground(new Color(0x40b6e0));
|
||||||
panel13.setLayout(new BorderLayout());
|
panel13.setLayout(new BorderLayout());
|
||||||
|
|
||||||
//---- label6 ----
|
//---- label6 ----
|
||||||
@@ -502,6 +512,52 @@ class MoreComponentsPanel
|
|||||||
}
|
}
|
||||||
add(splitPane3, "cell 1 11 4 1,grow");
|
add(splitPane3, "cell 1 11 4 1,grow");
|
||||||
|
|
||||||
|
//---- panelLabel ----
|
||||||
|
panelLabel.setText("JPanel:");
|
||||||
|
add(panelLabel, "cell 0 12");
|
||||||
|
|
||||||
|
//======== panel5 ========
|
||||||
|
{
|
||||||
|
panel5.putClientProperty("FlatLaf.style", "arc: 16; background: darken($Panel.background,5%)");
|
||||||
|
panel5.setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
//---- label9 ----
|
||||||
|
label9.setText("rounded background");
|
||||||
|
label9.setHorizontalAlignment(SwingConstants.CENTER);
|
||||||
|
panel5.add(label9, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
add(panel5, "cell 1 12 4 1,growy,width 150");
|
||||||
|
|
||||||
|
//======== panel4 ========
|
||||||
|
{
|
||||||
|
panel4.putClientProperty("FlatLaf.style", "border: 1,1,1,1,@disabledForeground,1,16; background: darken($Panel.background,5%)");
|
||||||
|
panel4.setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
//---- label8 ----
|
||||||
|
label8.setText("rounded border");
|
||||||
|
label8.setHorizontalAlignment(SwingConstants.CENTER);
|
||||||
|
panel4.add(label8, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
add(panel4, "cell 1 12 4 1,growy,width 150");
|
||||||
|
|
||||||
|
//---- labelLabel ----
|
||||||
|
labelLabel.setText("JLabel:");
|
||||||
|
add(labelLabel, "cell 0 13");
|
||||||
|
|
||||||
|
//---- label13 ----
|
||||||
|
label13.setText("rounded background");
|
||||||
|
label13.putClientProperty("FlatLaf.style", "arc: 999; border: 2,10,2,10");
|
||||||
|
label13.setBackground(new Color(0xb8e4f3));
|
||||||
|
label13.setForeground(new Color(0x135b76));
|
||||||
|
add(label13, "cell 1 13 4 1");
|
||||||
|
|
||||||
|
//---- label10 ----
|
||||||
|
label10.setText("rounded border");
|
||||||
|
label10.putClientProperty("FlatLaf.style", "arc: 999; border: 2,10,2,10,#135b76");
|
||||||
|
label10.setBackground(new Color(0xb8e4f3));
|
||||||
|
label10.setForeground(new Color(0x135b76));
|
||||||
|
add(label10, "cell 1 13 4 1");
|
||||||
|
|
||||||
//---- buttonGroup1 ----
|
//---- buttonGroup1 ----
|
||||||
ButtonGroup buttonGroup1 = new ButtonGroup();
|
ButtonGroup buttonGroup1 = new ButtonGroup();
|
||||||
buttonGroup1.add(toggleButton1);
|
buttonGroup1.add(toggleButton1);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
JFDML JFormDesigner: "7.0.5.0.404" Java: "17.0.2" encoding: "UTF-8"
|
JFDML JFormDesigner: "8.2.2.0.9999" Java: "21.0.1" encoding: "UTF-8"
|
||||||
|
|
||||||
new FormModel {
|
new FormModel {
|
||||||
contentType: "form/swing"
|
contentType: "form/swing"
|
||||||
@@ -9,7 +9,7 @@ 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 dialog,hidemode 3"
|
"$layoutConstraints": "insets dialog,hidemode 3"
|
||||||
"$columnConstraints": "[][][][][]"
|
"$columnConstraints": "[][][][][]"
|
||||||
"$rowConstraints": "[][][][][][][][][][][][100,top]"
|
"$rowConstraints": "[][][][][][][][][][][][100,top][50,top][]"
|
||||||
} ) {
|
} ) {
|
||||||
name: "this"
|
name: "this"
|
||||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||||
@@ -467,6 +467,62 @@ new FormModel {
|
|||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 1 11 4 1,grow"
|
"value": "cell 1 11 4 1,grow"
|
||||||
} )
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||||
|
name: "panelLabel"
|
||||||
|
"text": "JPanel:"
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 0 12"
|
||||||
|
} )
|
||||||
|
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
|
||||||
|
name: "panel5"
|
||||||
|
"$client.FlatLaf.style": "arc: 16; background: darken($Panel.background,5%)"
|
||||||
|
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||||
|
name: "label9"
|
||||||
|
"text": "rounded background"
|
||||||
|
"horizontalAlignment": 0
|
||||||
|
}, new FormLayoutConstraints( class java.lang.String ) {
|
||||||
|
"value": "Center"
|
||||||
|
} )
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 1 12 4 1,growy,width 150"
|
||||||
|
} )
|
||||||
|
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
|
||||||
|
name: "panel4"
|
||||||
|
"$client.FlatLaf.style": "border: 1,1,1,1,@disabledForeground,1,16; background: darken($Panel.background,5%)"
|
||||||
|
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||||
|
name: "label8"
|
||||||
|
"text": "rounded border"
|
||||||
|
"horizontalAlignment": 0
|
||||||
|
}, new FormLayoutConstraints( class java.lang.String ) {
|
||||||
|
"value": "Center"
|
||||||
|
} )
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 1 12 4 1,growy,width 150"
|
||||||
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||||
|
name: "labelLabel"
|
||||||
|
"text": "JLabel:"
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 0 13"
|
||||||
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||||
|
name: "label13"
|
||||||
|
"text": "rounded background"
|
||||||
|
"$client.FlatLaf.style": "arc: 999; border: 2,10,2,10"
|
||||||
|
"background": new java.awt.Color( 184, 228, 243, 255 )
|
||||||
|
"foreground": new java.awt.Color( 19, 91, 118, 255 )
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 1 13 4 1"
|
||||||
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||||
|
name: "label10"
|
||||||
|
"text": "rounded border"
|
||||||
|
"$client.FlatLaf.style": "arc: 999; border: 2,10,2,10,#135b76"
|
||||||
|
"background": new java.awt.Color( 184, 228, 243, 255 )
|
||||||
|
"foreground": new java.awt.Color( 19, 91, 118, 255 )
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 1 13 4 1"
|
||||||
|
} )
|
||||||
}, new FormLayoutConstraints( null ) {
|
}, new FormLayoutConstraints( null ) {
|
||||||
"location": new java.awt.Point( 0, 0 )
|
"location": new java.awt.Point( 0, 0 )
|
||||||
"size": new java.awt.Dimension( 700, 550 )
|
"size": new java.awt.Dimension( 700, 550 )
|
||||||
@@ -474,7 +530,7 @@ new FormModel {
|
|||||||
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
|
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
|
||||||
name: "buttonGroup1"
|
name: "buttonGroup1"
|
||||||
}, new FormLayoutConstraints( null ) {
|
}, new FormLayoutConstraints( null ) {
|
||||||
"location": new java.awt.Point( 0, 560 )
|
"location": new java.awt.Point( 0, 600 )
|
||||||
} )
|
} )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1.5 14.5L6 10M14.5 1.5L9.99998 6.00001M2.5 9.50001H6.5L6.5 13.5M13.5 6.5L9.5 6.50003V2.5" stroke="#6E6E6E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 396 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6.5 9.5L2 14M9.50003 6.50004L14 2.00001M5.5 14.5H1.5V10.5M10.5 1.5L14.5 1.50002V5.5" stroke="#6E6E6E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 391 B |
@@ -547,6 +547,12 @@ public class FlatInspector
|
|||||||
appendRow( buf, "Left-to-right", String.valueOf( c.getComponentOrientation().isLeftToRight() ) );
|
appendRow( buf, "Left-to-right", String.valueOf( c.getComponentOrientation().isLeftToRight() ) );
|
||||||
appendRow( buf, "Parent", (c.getParent() != null ? toString( c.getParent().getClass(), classHierarchy ) : "null") );
|
appendRow( buf, "Parent", (c.getParent() != null ? toString( c.getParent().getClass(), classHierarchy ) : "null") );
|
||||||
|
|
||||||
|
if( c instanceof JComponent ) {
|
||||||
|
Object style = ((JComponent)c).getClientProperty( FlatClientProperties.STYLE );
|
||||||
|
if( style != null )
|
||||||
|
appendRow( buf, "FlatLaf Style", style.toString() );
|
||||||
|
}
|
||||||
|
|
||||||
// append parent level
|
// append parent level
|
||||||
buf.append( "<tr><td colspan=\"2\">" );
|
buf.append( "<tr><td colspan=\"2\">" );
|
||||||
if( parentLevel > 0 )
|
if( parentLevel > 0 )
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import java.awt.Image;
|
|||||||
import java.awt.Paint;
|
import java.awt.Paint;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
|
import java.awt.LinearGradientPaint;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.RGBImageFilter;
|
import java.awt.image.RGBImageFilter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -62,6 +63,8 @@ public class FlatSVGIcon
|
|||||||
extends ImageIcon
|
extends ImageIcon
|
||||||
implements DisabledIconProvider
|
implements DisabledIconProvider
|
||||||
{
|
{
|
||||||
|
private static boolean loggingEnabled = true;
|
||||||
|
private static boolean svgCacheEnabled = true;
|
||||||
// cache that uses soft references for values, which allows freeing SVG documents if no longer used
|
// cache that uses soft references for values, which allows freeing SVG documents if no longer used
|
||||||
private static final SoftCache<String, SVGDocument> svgCache = new SoftCache<>();
|
private static final SoftCache<String, SVGDocument> svgCache = new SoftCache<>();
|
||||||
private static final SVGLoader svgLoader = new SVGLoader();
|
private static final SVGLoader svgLoader = new SVGLoader();
|
||||||
@@ -271,7 +274,8 @@ public class FlatSVGIcon
|
|||||||
|
|
||||||
if( document == null ) {
|
if( document == null ) {
|
||||||
loadFailed = true;
|
loadFailed = true;
|
||||||
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: failed to load SVG icon from input stream", null );
|
if( loggingEnabled )
|
||||||
|
LoggingFacade.INSTANCE.logConfig( "FlatSVGIcon: failed to load SVG icon from input stream", null );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,8 +453,9 @@ public class FlatSVGIcon
|
|||||||
* @param colorFilter The color filter
|
* @param colorFilter The color filter
|
||||||
* @since 1.2
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public void setColorFilter( ColorFilter colorFilter ) {
|
public FlatSVGIcon setColorFilter( ColorFilter colorFilter ) {
|
||||||
this.colorFilter = colorFilter;
|
this.colorFilter = colorFilter;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update() {
|
private void update() {
|
||||||
@@ -474,7 +479,8 @@ public class FlatSVGIcon
|
|||||||
|
|
||||||
if( url == null ) {
|
if( url == null ) {
|
||||||
loadFailed = true;
|
loadFailed = true;
|
||||||
LoggingFacade.INSTANCE.logConfig( "FlatSVGIcon: resource '" + name + "' not found (if using Java modules, check whether icon package is opened in module-info.java)", null );
|
if( loggingEnabled )
|
||||||
|
LoggingFacade.INSTANCE.logConfig( "FlatSVGIcon: resource '" + name + "' not found (if using Java modules, check whether icon package is opened in module-info.java)", null );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,6 +490,9 @@ public class FlatSVGIcon
|
|||||||
}
|
}
|
||||||
|
|
||||||
static synchronized SVGDocument loadSVG( URL url ) {
|
static synchronized SVGDocument loadSVG( URL url ) {
|
||||||
|
if( !svgCacheEnabled )
|
||||||
|
return loadSVGUncached( url );
|
||||||
|
|
||||||
// get from our cache
|
// get from our cache
|
||||||
String cacheKey = url.toString();
|
String cacheKey = url.toString();
|
||||||
SVGDocument document = svgCache.get( cacheKey );
|
SVGDocument document = svgCache.get( cacheKey );
|
||||||
@@ -491,18 +500,25 @@ public class FlatSVGIcon
|
|||||||
return document;
|
return document;
|
||||||
|
|
||||||
// load SVG document
|
// load SVG document
|
||||||
document = svgLoader.load( url );
|
document = loadSVGUncached( url );
|
||||||
|
|
||||||
if( document == null ) {
|
|
||||||
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: failed to load '" + url + "'", null );
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
svgCache.put( cacheKey, document );
|
svgCache.put( cacheKey, document );
|
||||||
|
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static SVGDocument loadSVGUncached( URL url ) {
|
||||||
|
SVGDocument document = svgLoader.load( url );
|
||||||
|
|
||||||
|
if( document == null ) {
|
||||||
|
if( loggingEnabled )
|
||||||
|
LoggingFacade.INSTANCE.logConfig( "FlatSVGIcon: failed to load '" + url + "'", null );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
private URL getIconURL( String name, boolean dark ) {
|
private URL getIconURL( String name, boolean dark ) {
|
||||||
if( dark ) {
|
if( dark ) {
|
||||||
int dotIndex = name.lastIndexOf( '.' );
|
int dotIndex = name.lastIndexOf( '.' );
|
||||||
@@ -611,7 +627,10 @@ public class FlatSVGIcon
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void paintSvgError( Graphics2D g, int x, int y ) {
|
private void paintSvgError( Graphics2D g, int x, int y ) {
|
||||||
g.setColor( Color.red );
|
if( g instanceof GraphicsFilter )
|
||||||
|
((GraphicsFilter)g).setColorUnfiltered( Color.red );
|
||||||
|
else
|
||||||
|
g.setColor( Color.red );
|
||||||
g.fillRect( x, y, getIconWidth(), getIconHeight() );
|
g.fillRect( x, y, getIconWidth(), getIconHeight() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,6 +711,34 @@ public class FlatSVGIcon
|
|||||||
darkLaf = FlatLaf.isLafDark();
|
darkLaf = FlatLaf.isLafDark();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.4.1 */
|
||||||
|
public static boolean isLoggingEnabled() {
|
||||||
|
return loggingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.4.1 */
|
||||||
|
public static void setLoggingEnabled( boolean loggingEnabled ) {
|
||||||
|
FlatSVGIcon.loggingEnabled = loggingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.4.1 */
|
||||||
|
public static boolean isSVGDocumentEnabled() {
|
||||||
|
return svgCacheEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.4.1 */
|
||||||
|
public static void setSVGDocumentEnabled( boolean svgCacheEnabled ) {
|
||||||
|
FlatSVGIcon.svgCacheEnabled = svgCacheEnabled;
|
||||||
|
|
||||||
|
if( !svgCacheEnabled )
|
||||||
|
clearSVGDocumentCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.4.1 */
|
||||||
|
public static void clearSVGDocumentCache() {
|
||||||
|
svgCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
//---- class ColorFilter --------------------------------------------------
|
//---- class ColorFilter --------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -978,10 +1025,23 @@ public class FlatSVGIcon
|
|||||||
super.setColor( filterColor( c ) );
|
super.setColor( filterColor( c ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setColorUnfiltered( Color c ) {
|
||||||
|
super.setColor( c );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPaint( Paint paint ) {
|
public void setPaint( Paint paint ) {
|
||||||
if( paint instanceof Color )
|
if( paint instanceof Color )
|
||||||
paint = filterColor( (Color) paint );
|
paint = filterColor( (Color) paint );
|
||||||
|
else if( paint instanceof LinearGradientPaint ) {
|
||||||
|
LinearGradientPaint oldPaint = (LinearGradientPaint) paint;
|
||||||
|
Color[] newColors = filterColors( oldPaint.getColors() );
|
||||||
|
if( newColors != null ) {
|
||||||
|
paint = new LinearGradientPaint( oldPaint.getStartPoint(), oldPaint.getEndPoint(),
|
||||||
|
oldPaint.getFractions(), newColors, oldPaint.getCycleMethod(),
|
||||||
|
oldPaint.getColorSpace(), oldPaint.getTransform() );
|
||||||
|
}
|
||||||
|
}
|
||||||
super.setPaint( paint );
|
super.setPaint( paint );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1001,5 +1061,15 @@ public class FlatSVGIcon
|
|||||||
}
|
}
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Color[] filterColors( Color[] colors ) {
|
||||||
|
Color[] newColors = new Color[colors.length];
|
||||||
|
boolean changed = false;
|
||||||
|
for( int i = 0; i < colors.length; i++ ) {
|
||||||
|
newColors[i] = filterColor( colors[i] );
|
||||||
|
changed = (changed || newColors[i] != colors[i]);
|
||||||
|
}
|
||||||
|
return changed ? newColors : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.extras.components;
|
package com.formdev.flatlaf.extras.components;
|
||||||
|
|
||||||
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import javax.swing.JSplitPane;
|
import javax.swing.JSplitPane;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +27,29 @@ import javax.swing.JSplitPane;
|
|||||||
*/
|
*/
|
||||||
public class FlatSplitPane
|
public class FlatSplitPane
|
||||||
extends JSplitPane
|
extends JSplitPane
|
||||||
implements FlatStyleableComponent
|
implements FlatComponentExtension, FlatStyleableComponent
|
||||||
{
|
{
|
||||||
|
// NOTE: enum names must be equal to allowed strings
|
||||||
|
/** @since 3.4.1 */ public enum ExpandableSide { both, left, right }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns what side of the spilt pane is allowed to expand
|
||||||
|
* via one-touch expanding arrow buttons.
|
||||||
|
*
|
||||||
|
* @since 3.4.1
|
||||||
|
*/
|
||||||
|
public ExpandableSide getExpandableSide() {
|
||||||
|
return getClientPropertyEnumString( SPLIT_PANE_EXPANDABLE_SIDE, ExpandableSide.class,
|
||||||
|
null, ExpandableSide.both );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies what side of the spilt pane is allowed to expand
|
||||||
|
* via one-touch expanding arrow buttons.
|
||||||
|
*
|
||||||
|
* @since 3.4.1
|
||||||
|
*/
|
||||||
|
public void setExpandableSide( ExpandableSide expandableSide ) {
|
||||||
|
putClientPropertyEnumString( SPLIT_PANE_EXPANDABLE_SIDE, expandableSide );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
// For maven compatibility, <font-version> should be in format <major>.<minor>[.<micro>].
|
// For maven compatibility, <font-version> should be in format <major>.<minor>[.<micro>].
|
||||||
// <build-number> is optional and should be incremented only if a new release is
|
// <build-number> is optional and should be incremented only if a new release is
|
||||||
// necessary, but the <font-version> has not changed.
|
// necessary, but the <font-version> has not changed.
|
||||||
version = "3.19"
|
version = "4.0"
|
||||||
|
|
||||||
if( !rootProject.hasProperty( "release" ) )
|
if( !rootProject.hasProperty( "release" ) )
|
||||||
version = version.toString() + "-SNAPSHOT"
|
version = version.toString() + "-SNAPSHOT"
|
||||||
@@ -33,8 +33,8 @@ plugins {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation( project( ":flatlaf-core" ) )
|
implementation( project( ":flatlaf-core" ) )
|
||||||
|
|
||||||
testImplementation( libs.bundles.junit )
|
testImplementation( libs.junit )
|
||||||
testRuntimeOnly( libs.junit.engine )
|
testRuntimeOnly( libs.junit.launcher )
|
||||||
}
|
}
|
||||||
|
|
||||||
flatlafModuleInfo {
|
flatlafModuleInfo {
|
||||||
@@ -56,7 +56,7 @@ tasks {
|
|||||||
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||||
}
|
}
|
||||||
|
|
||||||
withType<PublishToMavenRepository>().configureEach {
|
withType<AbstractPublishToMaven>().configureEach {
|
||||||
onlyIf { !rootProject.hasProperty( "skipFonts" ) }
|
onlyIf { !rootProject.hasProperty( "skipFonts" ) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,8 +73,8 @@ publishing {
|
|||||||
pom {
|
pom {
|
||||||
licenses {
|
licenses {
|
||||||
license {
|
license {
|
||||||
name.set( "SIL OPEN FONT LICENSE Version 1.1" )
|
name = "SIL OPEN FONT LICENSE Version 1.1"
|
||||||
url.set( "https://choosealicense.com/licenses/ofl-1.1/" )
|
url = "https://choosealicense.com/licenses/ofl-1.1/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ public class FlatInterFont
|
|||||||
* new Font( FlatInterFont.FAMILY_SEMIBOLD, Font.ITALIC, 12 );
|
* new Font( FlatInterFont.FAMILY_SEMIBOLD, Font.ITALIC, 12 );
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*/
|
*/
|
||||||
public static final String FAMILY_SEMIBOLD = "Inter Semi Bold";
|
public static final String FAMILY_SEMIBOLD = "Inter SemiBold";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use for {@link #installStyle(String)} to install single font style.
|
* Use for {@link #installStyle(String)} to install single font style.
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user