Compare commits

..

60 Commits

Author SHA1 Message Date
Karl Tauber
ca3b2b4b07 Fonts: updated Inter to v4.0 2024-01-24 15:45:43 +01:00
Karl Tauber
722dde63df Native libraries: system property flatlaf.nativeLibraryPath now supports loading native libraries named the same as on Maven central; improved log messages for loading fails (issue #797) 2024-01-24 01:02:44 +01:00
Karl Tauber
c85baf4dc6 flatlaf-natives-windows: fixed invocation of GetMonitorInfo() (issue #798)
also removed unnecessary setting `mii.fType`
2024-01-23 15:54:44 +01:00
Karl Tauber
96b7770ab2 CHANGELOG.md: added PR #794 2024-01-23 14:29:22 +01:00
rogerbj
0c00117820 Remove the dependency with JMenuBar to support, for example, the CommandMenuBar in JIDE OSS
(cherry picked from commit 4d4b90c989)
2024-01-23 13:47:12 +01:00
Karl Tauber
eed11d211b GitHub Actions: updated versions of used actions 2024-01-11 23:07:21 +01:00
Karl Tauber
19f27a8d56 release 3.3 2024-01-11 18:14:17 +01:00
Karl Tauber
cf3fa17666 fixed typos and grammar 2024-01-11 18:11:09 +01:00
Karl Tauber
6fdc56f2d3 Merge PR #790: Table header background is never restored after hover when the renderer background is null 2024-01-10 19:26:22 +01:00
Karl Tauber
9f17a5b26d TableHeader: restore also renderer foreground after hover 2024-01-10 19:19:01 +01:00
Karl Tauber
fa53e90847 Merge PR #788: Support for hover/pressed on SplitPane divider 2024-01-10 16:43:28 +01:00
Karl Tauber
50c630f403 Table: fixed border arc of components in complex table cell editors (issue #786) 2024-01-10 16:37:32 +01:00
Dar
5630c161ea fix: restore the renderer background after hover
The table header background is never restored after hover when the label renderer is opaque
2024-01-09 16:58:21 +01:00
Karl Tauber
7d16ff9e79 SplitPane:
- removed `SplitPaneDivider.showHover` (hover is now enabled if hover color is specified)
- added `SplitPaneDivider.pressedColor` (for pressed/dragging in continuous layout)
- fixed painting of dragging divider in non-continuous layout (was 1px too small; added grip)
- updated styling unit tests
2024-01-08 22:39:01 +01:00
Karl Tauber
a9ea9daec3 FileChooser: catch NPE in Java 21 when getting icon for .exe files that use default Windows exe icon (see https://bugs.openjdk.org/browse/JDK-8320692) 2024-01-08 20:29:20 +01:00
Karl Tauber
45bdd40dce Viewport: use method handle, instead of reflection, to get view UI faster 2024-01-08 20:12:26 +01:00
Karl Tauber
a2bca88eec removed support for JetBrains custom decorations 2024-01-08 16:22:20 +01:00
Karl Tauber
c0dd02ee13 TabbedPane: paint rounded tab area background for rounded cards (issue #717) 2024-01-08 13:22:59 +01:00
Karl Tauber
97495a6093 TabbedPane: avoid "jumping" tab area when switching L&F or when mouse wheel scrolling to last tab (regression in commit 4ad45088c4) 2023-12-31 12:56:43 +01:00
Karl Tauber
4ad45088c4 TabbedPane: fixed "endless" layouting and repainting when using nested tabbed panes (top and bottom tab placement) and RSyntaxTextArea (with enabled line-wrapping) as tab content (see https://github.com/skylot/jadx/issues/2030)
instead of using `BasicTabbedPaneUI.TabbedPaneLayout.layoutContainer()`, now layouting all components ourself and avoid double moving/resizing of some components
2023-12-30 12:56:58 +01:00
Dar
d6d1d4b1b7 fix: missing properties in the expected map 2023-12-29 15:12:06 +01:00
Dar
6e453c170f new: support for hover on splitpane divider 2023-12-29 12:38:34 +01:00
Karl Tauber
beb2deee52 TabbedPane: further reduced duplicate code in nested classes FlatTabbedPaneLayout and FlatTabbedPaneScrollLayout 2023-12-23 14:06:47 +01:00
Karl Tauber
9c69043b2c TabbedPane: reduced duplicate code in nested classes FlatTabbedPaneLayout and FlatTabbedPaneScrollLayout 2023-12-23 00:28:44 +01:00
Karl Tauber
4df34b3f9d TableHeader: no longer temporary replace header cell renderer while painting (issue https://github.com/apache/netbeans/issues/6835) 2023-12-22 14:08:49 +01:00
Karl Tauber
ee01756188 TabbedPane:
- avoid unnecessary repainting whole tabbed pane content area when layouting leading/trailing components
- avoid unnecessary repainting of selected tab on temporary changes
2023-12-21 18:59:11 +01:00
Karl Tauber
0386aaa18b Merge PR #758: TabbedPane: support rotated/vertical tabs 2023-12-21 17:41:49 +01:00
Karl Tauber
92c4230cde Merge PR #713: Scrollpane rounded border 2023-12-21 17:40:00 +01:00
Karl Tauber
26165999e0 Table: fixed background of boolean columns when using alternating row colors (issue #780) 2023-12-21 16:58:49 +01:00
Karl Tauber
38b2641078 NativeLibrary: in development environment, load native library from 'src' folder 2023-12-14 15:29:02 +01:00
Karl Tauber
4e19169312 Merge PR #772: macOS: native rounded borders for popups 2023-12-14 15:19:01 +01:00
Karl Tauber
46de81c1c9 macOS native:
- removed `FlatNativeMacLibrary.getWindowPtr()` because it is too dangerous to use `windowPtr` (which is `NSWindow*`) in Java (using invalid window pointer would crash app)
- made `getNSWindow()` 20x faster
- catch exceptions in `getNSWindow()`
- digitally signed dylibs
2023-12-14 14:39:26 +01:00
Karl Tauber
9bf4da7acf Native macOS libraries updated for PR #772
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/7151969281
2023-12-09 17:04:03 +01:00
Karl Tauber
6f32236fb7 macOS: native rounded borders for popups (issue #715) 2023-12-09 16:12:35 +01:00
Karl Tauber
c25d857e78 UI defaults dumps updated on macOS for commits 9fef2f9d05 and ce527329a6 2023-12-08 19:07:32 +01:00
Karl Tauber
8cc2925fd0 FlatPopupFactory: reordered some methods (nothing else changed) 2023-12-08 17:53:51 +01:00
Karl Tauber
2b87d3c4db Native window libraries: updated Windows DLLs for commit 7f6f366744
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/7132082543

locally signed Windows DLLs with FormDev Software code signing certificate
2023-12-07 19:02:43 +01:00
Karl Tauber
7f6f366744 flatlaf-natives-windows: support DWM attributes DWMWA_USE_IMMERSIVE_DARK_MODE, DWMWA_CAPTION_COLOR and DWMWA_TEXT_COLOR (all unused in FlatLaf core) 2023-12-07 18:53:45 +01:00
Karl Tauber
b1fdbde5cd ScrollPane: allow specifying scroll pane border arc for multi-line text components, lists, tables and trees 2023-11-25 23:29:39 +01:00
Karl Tauber
417f0f5f1c fixed broken rendering after resizing window to minimum size and then increasing size again (issue #767) 2023-11-25 19:14:39 +01:00
Karl Tauber
ec7673790c Demo: added macOS themes to control bar combobox (F5 and F6) 2023-11-14 11:42:54 +01:00
Karl Tauber
7d0bdf3b9e OptionPane: fixed styling custom panel background in JOptionPane (issue #761) 2023-11-14 10:38:28 +01:00
Karl Tauber
2ef5270095 TabbedPane: support rotated/vertical tabs (issue #633) 2023-11-04 19:55:01 +01:00
Karl Tauber
61ba011c3b Testing: introduced FlatTestEnumSelector (a toolbar with a single button group) for easier testing 2023-11-01 14:53:06 +01:00
Karl Tauber
8d8b9f3e98 macOS themes: fixed ToolBar.hoverButtonGroupArc 2023-11-01 14:52:17 +01:00
Karl Tauber
69899ec29f ToolBar: added styling properties separatorWidth and separatorColor 2023-11-01 12:55:06 +01:00
Karl Tauber
5063621c95 junit updated to 5.10.0 2023-11-01 12:07:03 +01:00
Karl Tauber
030177f739 Theme Editore: updated rsyntaxtextarea and autocomplete to latest versions 2023-10-31 16:58:32 +01:00
Karl Tauber
808f5a6381 Button and ToggleButton: selected buttons did not use explicitly set foreground color (issue #756) 2023-10-31 16:14:48 +01:00
Karl Tauber
9602b191a9 jide-oss: updated to 3.7.14 2023-10-22 17:56:36 +02:00
Karl Tauber
34bd2d781c Table: switching theme looses table grid and intercell spacing (issues #733 and #750) 2023-10-22 17:55:10 +02:00
Karl Tauber
cf364c1264 release 3.2.5 2023-10-21 23:53:46 +02:00
Karl Tauber
a997820bb6 Merge PR #754: Fixing NPE when showing a popup without an invoker on Windows 10 2023-10-21 23:52:02 +02:00
Eduwardo Horibe
b8fabd59c0 Fixing NPE when showing a popup without an invoker on Windows 10 2023-10-21 15:26:55 -03:00
Karl Tauber
97d290795e release 3.2.4 2023-10-21 18:23:17 +02:00
Karl Tauber
2a237ff5fc Popup: fixed potential NPE in (unusual) case that the popup invoker is null (only on Linux with Wayland and Java 21; regression in 3.2.3) (issue #752) 2023-10-21 18:21:59 +02:00
Karl Tauber
0c604b1023 ScrollPane: increase viewport width for rounded border to remove/reduce gap between view and vertical scrollbar 2023-08-27 18:14:03 +02:00
Karl Tauber
40418607e5 ScrollPane: fixed lost styling on ScrollPane border if using Table as view component 2023-08-13 23:23:03 +02:00
Karl Tauber
5436ea88d8 ScrollPane: improved/fixed calculation of left/right padding for rounded border 2023-08-13 17:01:56 +02:00
Karl Tauber
7bec5ec6dc ScrollPane: support rounded border 2023-08-13 14:01:09 +02:00
148 changed files with 5867 additions and 2264 deletions

View File

@@ -19,7 +19,7 @@ jobs:
# test against # test against
# - Java 8 (minimum requirement) # - Java 8 (minimum requirement)
# - Java LTS versions (11, 17, ...) # - Java LTS versions (11, 17, ...)
# - lastest Java version(s) # - latest Java version(s)
java: java:
- 8 - 8
- 11 # LTS - 11 # LTS
@@ -30,13 +30,13 @@ jobs:
toolchain: 21 # latest toolchain: 21 # latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1 - uses: gradle/wrapper-validation-action@v1
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 and 17 are pre-installed on ubuntu-latest
@@ -50,7 +50,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 +70,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 +106,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

View File

@@ -30,10 +30,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

View File

@@ -20,17 +20,18 @@ jobs:
matrix: matrix:
os: os:
- windows - windows
- macos
- ubuntu - ubuntu
runs-on: ${{ matrix.os }}-latest runs-on: ${{ matrix.os }}-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1 - uses: gradle/wrapper-validation-action@v1
- name: Setup Java 11 - name: Setup Java 11
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
java-version: 11 java-version: 11
distribution: temurin distribution: temurin
@@ -42,7 +43,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: |

View File

@@ -1,6 +1,97 @@
FlatLaf Change Log FlatLaf Change Log
================== ==================
## 3.4-SNAPSHOT
#### New features and improvements
- Native libraries: System property `flatlaf.nativeLibraryPath` now supports
loading native libraries named the same as on Maven central. Improved log
messages for loading fails.
- Fonts: Updated **Inter** to
[v4.0](https://github.com/rsms/inter/releases/tag/v4.0).
#### Fixed bugs
- JIDE CommandMenuBar: Fixed `ClassCastException` when JIDE command bar displays
`JideMenu` in popup. (PR #794)
## 3.3
#### New features and improvements
- macOS (10.14+): Popups (`JPopupMenu`, `JComboBox`, `JToolTip`, etc.) now use
native macOS rounded borders. (PR #772; issue #715)
- Native libraries: Added `libflatlaf-macos-arm64.dylib` and
`libflatlaf-macos-x86_64.dylib`. See also
https://www.formdev.com/flatlaf/native-libraries/.
- ScrollPane: Support rounded border. (PR #713)
- SplitPane: Support divider hover and pressed background colors. (PR #788)
- TabbedPane: Support vertical tabs. (PR #758, issue #633)
- TabbedPane: Paint rounded tab area background for rounded cards. (issue #717)
- ToolBar: Added styling properties `separatorWidth` and `separatorColor`.
#### Fixed bugs
- Button and ToggleButton: Selected buttons did not use explicitly set
foreground color. (issue 756)
- FileChooser: Catch NPE in Java 21 when getting icon for `.exe` files that use
default Windows exe icon. (see
[JDK-8320692](https://bugs.openjdk.org/browse/JDK-8320692))
- OptionPane: Fixed styling custom panel background in `JOptionPane`. (issue
#761)
- ScrollPane: Styling ScrollPane border properties did not work if view
component is a Table.
- Table:
- Switching theme looses table grid and intercell spacing. (issues #733 and
#750)
- Fixed background of `boolean` columns when using alternating row colors.
(issue #780)
- Fixed border arc of components in complex table cell editors. (issue #786)
- TableHeader:
- No longer temporary replace header cell renderer while painting. This avoids
a `StackOverflowError` in case that custom renderer does this too. (see
[NetBeans issue #6835](https://github.com/apache/netbeans/issues/6835)) This
also improves compatibility with custom table header implementations.
- Header cell renderer background/foreground colors were not restored after
hover if renderer uses `null` for background/foreground. (PR #790)
- TabbedPane:
- Avoid unnecessary repainting whole tabbed pane content area when layouting
leading/trailing components.
- Avoid unnecessary repainting of selected tab on temporary changes.
- Fixed "endless" layouting and repainting when using nested tabbed panes (top
and bottom tab placement) and RSyntaxTextArea (with enabled line-wrapping)
as tab content. (see
[jadx issue #2030](https://github.com/skylot/jadx/issues/2030))
- Fixed broken rendering after resizing window to minimum size and then
increasing size again. (issue #767)
#### Incompatibilities
- Removed support for JetBrains custom decorations, which required
[JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/wiki) (JBR)
8 or 11. It did not work for JBR 17. System property
`flatlaf.useJetBrainsCustomDecorations` is now ignored. **Note**: FlatLaf
window decorations continue to work with JBR.
## 3.2.5
#### Fixed bugs
- Popup: Fixed NPE if popup invoker is `null` on Windows 10. (issue #753;
regression in 3.2.1 in fix for #626)
## 3.2.4
#### Fixed bugs
- Popup: Fixed NPE if popup invoker is `null` on Linux with Wayland and Java 21.
(issue #752; regression in 3.2.3)
## 3.2.3 ## 3.2.3
#### Fixed bugs #### Fixed bugs
@@ -162,7 +253,6 @@ FlatLaf Change Log
- Windows DLLs are now digitally signed with FormDev Software GmbH - Windows DLLs are now digitally signed with FormDev Software GmbH
certificate. certificate.
#### Fixed bugs #### Fixed bugs
- FlatLaf window decorations: - FlatLaf window decorations:

View File

@@ -62,7 +62,7 @@ build script:
artifactId: flatlaf artifactId: flatlaf
version: (see button below) version: (see button below)
Otherwise download `flatlaf-<version>.jar` here: Otherwise, download `flatlaf-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf)
@@ -141,7 +141,6 @@ details and downloads.
Buzz Buzz
---- ----
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
- [FlatLaf 3.1 (and 3.0) announcement on Reddit](https://www.reddit.com/r/java/comments/12xgrsu/flatlaf_31_and_30_swing_look_and_feel/) - [FlatLaf 3.1 (and 3.0) announcement on Reddit](https://www.reddit.com/r/java/comments/12xgrsu/flatlaf_31_and_30_swing_look_and_feel/)
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/) - [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/) - [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)

View File

@@ -45,7 +45,7 @@ public class ReorderJarEntries
// 1st pass: copy .properties files // 1st pass: copy .properties files
copyFiles( zipOutStream, jarFile, name -> name.endsWith( ".properties" ) ); copyFiles( zipOutStream, jarFile, name -> name.endsWith( ".properties" ) );
// 2st pass: copy other files // 2nd pass: copy other files
copyFiles( zipOutStream, jarFile, name -> !name.endsWith( ".properties" ) ); copyFiles( zipOutStream, jarFile, name -> !name.endsWith( ".properties" ) );
} }

View File

@@ -127,9 +127,11 @@ flatlafPublish {
val natives = "src/main/resources/com/formdev/flatlaf/natives" val natives = "src/main/resources/com/formdev/flatlaf/natives"
nativeArtifacts = listOf( nativeArtifacts = listOf(
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ), NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ), NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ), NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),
NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ), NativeArtifact( "${natives}/libflatlaf-macos-arm64.dylib", "macos-arm64", "dylib" ),
NativeArtifact( "${natives}/libflatlaf-macos-x86_64.dylib", "macos-x86_64", "dylib" ),
NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ),
) )
} }

View File

@@ -1,5 +1,5 @@
#Signature file v4.1 #Signature file v4.1
#Version 3.2.3 #Version 3.3
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"
@@ -23,6 +23,7 @@ fld public final static java.lang.String PLACEHOLDER_TEXT = "JTextField.placehol
fld public final static java.lang.String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius" fld public final static java.lang.String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius"
fld public final static java.lang.String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted" fld public final static java.lang.String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted"
fld public final static java.lang.String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight" fld public final static java.lang.String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight"
fld public final static java.lang.String POPUP_ROUNDED_BORDER_WIDTH = "Popup.roundedBorderWidth"
fld public final static java.lang.String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight" fld public final static java.lang.String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight"
fld public final static java.lang.String PROGRESS_BAR_SQUARE = "JProgressBar.square" fld public final static java.lang.String PROGRESS_BAR_SQUARE = "JProgressBar.square"
fld public final static java.lang.String SCROLL_BAR_SHOW_BUTTONS = "JScrollBar.showButtons" fld public final static java.lang.String SCROLL_BAR_SHOW_BUTTONS = "JScrollBar.showButtons"
@@ -67,6 +68,11 @@ fld public final static java.lang.String TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT = "JT
fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight" fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight"
fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement" fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement"
fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets" fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION = "JTabbedPane.tabRotation"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_AUTO = "auto"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_LEFT = "left"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_NONE = "none"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_RIGHT = "right"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType" fld public final static java.lang.String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_CARD = "card" fld public final static java.lang.String TABBED_PANE_TAB_TYPE_CARD = "card"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined" fld public final static java.lang.String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined"
@@ -241,7 +247,7 @@ meth public void setExtraDefaults(java.util.Map<java.lang.String,java.lang.Strin
meth public void uninitialize() meth public void uninitialize()
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>) meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
supr javax.swing.plaf.basic.BasicLookAndFeel supr javax.swing.plaf.basic.BasicLookAndFeel
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,getUIMethod,getUIMethodInitialized,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,preferredFontFamily,preferredLightFontFamily,preferredMonospacedFontFamily,preferredSemiboldFontFamily,subMenuUsabilityHelperInstalled,systemColorGetter,uiDefaultsGetters,updateUIPending hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,preferredFontFamily,preferredLightFontFamily,preferredMonospacedFontFamily,preferredSemiboldFontFamily,subMenuUsabilityHelperInstalled,systemColorGetter,uiDefaultsGetters,updateUIPending
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
@@ -282,6 +288,7 @@ fld public final static java.lang.String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.ui
fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled" fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled"
fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange" fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange"
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations" fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
anno 0 java.lang.Deprecated()
fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary" fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"
fld public final static java.lang.String USE_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"

View File

@@ -33,7 +33,7 @@ public interface FlatClientProperties
//---- JButton ------------------------------------------------------------ //---- JButton ------------------------------------------------------------
/** /**
* Specifies type of a button. * Specifies type of button.
* <p> * <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br> * <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.String}<br> * <strong>Value type</strong> {@link java.lang.String}<br>
@@ -278,12 +278,13 @@ public interface FlatClientProperties
* <p> * <p>
* Note that this is not available on all platforms since it requires special support. * Note that this is not available on all platforms since it requires special support.
* Supported platforms: * Supported platforms:
* <p> * <ul>
* <strong>Windows 11</strong> (x86 or x86_64): Only two corner radiuses are supported * <li><strong>Windows 11</strong>: Only two corner radiuses are supported
* by the OS: {@code DWMWCP_ROUND} is 8px and {@code DWMWCP_ROUNDSMALL} is 4px. * by the OS: {@code DWMWCP_ROUND} is 8px and {@code DWMWCP_ROUNDSMALL} is 4px.
* If this value is {@code 1 - 4}, then {@code DWMWCP_ROUNDSMALL} is used. * If this value is {@code 1 - 4}, then {@code DWMWCP_ROUNDSMALL} is used.
* If it is {@code >= 5}, then {@code DWMWCP_ROUND} is used. * If it is {@code >= 5}, then {@code DWMWCP_ROUND} is used.
* <p> * <li><strong>macOS</strong> (10.14 and later): Any corner radius is supported.
* </ul>
* <strong>Component</strong> {@link javax.swing.JComponent}<br> * <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br> * <strong>Value type</strong> {@link java.lang.Integer}<br>
* *
@@ -291,6 +292,24 @@ public interface FlatClientProperties
*/ */
String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius"; String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius";
/**
* Specifies the popup rounded border width if the component is shown in a popup
* or if the component is the owner of another component that is shown in a popup.
* <p>
* Only used if popup uses rounded border.
* <p>
* Note that this is not available on all platforms since it requires special support.
* Supported platforms:
* <ul>
* <li><strong>macOS</strong> (10.14 and later)
* </ul>
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.Float}<br>
*
* @since 3.3
*/
String POPUP_ROUNDED_BORDER_WIDTH = "Popup.roundedBorderWidth";
/** /**
* Specifies whether a drop shadow is painted if the component is shown in a popup * Specifies whether a drop shadow is painted if the component is shown in a popup
* or if the component is the owner of another component that is shown in a popup. * or if the component is the owner of another component that is shown in a popup.
@@ -402,10 +421,10 @@ public interface FlatClientProperties
String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle"; String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle";
/** /**
* Specifies whether the "iconfify" button should be shown in the window title bar * Specifies whether the "iconify" button should be shown in the window title bar
* (requires enabled window decorations). Default is {@code true}. * (requires enabled window decorations). Default is {@code true}.
* <p> * <p>
* Setting this shows/hides the "iconfify" button * Setting this shows/hides the "iconify" button
* for the {@code JFrame} that contains the root pane. * for the {@code JFrame} that contains the root pane.
* <p> * <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br> * <strong>Component</strong> {@link javax.swing.JRootPane}<br>
@@ -487,7 +506,7 @@ public interface FlatClientProperties
* On macOS, Java supports this out of the box. * On macOS, Java supports this out of the box.
* <p> * <p>
* Note that this client property must be set before the window becomes displayable. * Note that this client property must be set before the window becomes displayable.
* Otherwise an {@link IllegalComponentStateException} is thrown. * Otherwise, an {@link IllegalComponentStateException} is thrown.
* <p> * <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br> * <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br> * <strong>Value type</strong> {@link java.lang.String}<br>
@@ -932,6 +951,59 @@ public interface FlatClientProperties
*/ */
String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement"; String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement";
/**
* Specifies the rotation of the tabs (title, icon, etc.).
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link SwingConstants#LEFT},
* {@link SwingConstants#RIGHT},
* {@link #TABBED_PANE_TAB_ROTATION_NONE}, (default)
* {@link #TABBED_PANE_TAB_ROTATION_AUTO},
* {@link #TABBED_PANE_TAB_ROTATION_LEFT} or
* {@link #TABBED_PANE_TAB_ROTATION_RIGHT}
*
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION = "JTabbedPane.tabRotation";
/**
* Tabs are not rotated.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_NONE = "none";
/**
* Tabs are rotated depending on tab placement.
* <p>
* For top and bottom tab placement, the tabs are not rotated.<br>
* For left tab placement, the tabs are rotated counter-clockwise.<br>
* For right tab placement, the tabs are rotated clockwise.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_AUTO = "auto";
/**
* Tabs are rotated counter-clockwise.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_LEFT = "left";
/**
* Tabs are rotated clockwise.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_RIGHT = "right";
/** /**
* Specifies a component that will be placed at the leading edge of the tabs area. * Specifies a component that will be placed at the leading edge of the tabs area.
* <p> * <p>

View File

@@ -71,7 +71,7 @@ class FlatInputMaps
); );
} }
// join ltr and rtl bindings to fix up/down/etc keys in right-to-left component orientation // join ltr and rtl bindings to fix up/down/etc. keys in right-to-left component orientation
Object[] bindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings" ); Object[] bindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings" );
Object[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" ); Object[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" );
if( bindings != null && rtlBindings != null ) { if( bindings != null && rtlBindings != null ) {

View File

@@ -30,9 +30,6 @@ import java.awt.image.ImageProducer;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.io.File; import java.io.File;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
@@ -78,6 +75,7 @@ import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
import com.formdev.flatlaf.ui.FlatPopupFactory; import com.formdev.flatlaf.ui.FlatPopupFactory;
import com.formdev.flatlaf.ui.FlatRootPaneUI; import com.formdev.flatlaf.ui.FlatRootPaneUI;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.ui.JavaCompatibility2;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.FontUtils; import com.formdev.flatlaf.util.FontUtils;
import com.formdev.flatlaf.util.GrayFilter; import com.formdev.flatlaf.util.GrayFilter;
@@ -183,17 +181,11 @@ public abstract class FlatLaf
* This depends on the operating system and on the used Java runtime. * This depends on the operating system and on the used Java runtime.
* <p> * <p>
* This method returns {@code true} on Windows 10/11 (see exception below) * This method returns {@code true} on Windows 10/11 (see exception below)
* and on Linux, {@code false} otherwise. * and on Linux, otherwise returns {@code false}.
* <p>
* Returns also {@code false} on Windows 10/11 if
* FlatLaf native window border support is available (requires Windows 10/11).
* <p> * <p>
* Returns also {@code false} on Windows 10/11 if:
* <ul>
* <li>FlatLaf native window border support is available (requires Windows 10/11)</li>
* <li>running in
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
* and JBR supports custom window decorations
* </li>
* </ul>
* In these cases, custom decorations are enabled by the root pane. * In these cases, custom decorations are enabled by the root pane.
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or * Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary. * {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
@@ -1295,8 +1287,8 @@ public abstract class FlatLaf
* @since 2.5 * @since 2.5
*/ */
public static Map<String, Class<?>> getStyleableInfos( JComponent c ) { public static Map<String, Class<?>> getStyleableInfos( JComponent c ) {
StyleableUI ui = getStyleableUI( c ); ComponentUI ui = JavaCompatibility2.getUI( c );
return (ui != null) ? ui.getStyleableInfos( c ) : null; return (ui instanceof StyleableUI) ? ((StyleableUI)ui).getStyleableInfos( c ) : null;
} }
/** /**
@@ -1308,41 +1300,10 @@ public abstract class FlatLaf
*/ */
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
public static <T> T getStyleableValue( JComponent c, String key ) { public static <T> T getStyleableValue( JComponent c, String key ) {
StyleableUI ui = getStyleableUI( c ); ComponentUI ui = JavaCompatibility2.getUI( c );
return (ui != null) ? (T) ui.getStyleableValue( c, key ) : null; return (ui instanceof StyleableUI) ? (T) ((StyleableUI)ui).getStyleableValue( c, key ) : null;
} }
private static StyleableUI getStyleableUI( JComponent c ) {
if( !getUIMethodInitialized ) {
getUIMethodInitialized = true;
if( SystemInfo.isJava_9_orLater ) {
try {
// JComponent.getUI() is available since Java 9
getUIMethod = MethodHandles.lookup().findVirtual( JComponent.class, "getUI",
MethodType.methodType( ComponentUI.class ) );
} catch( Exception ex ) {
// ignore
}
}
}
try {
Object ui;
if( getUIMethod != null )
ui = getUIMethod.invoke( c );
else
ui = c.getClass().getMethod( "getUI" ).invoke( c );
return (ui instanceof StyleableUI) ? (StyleableUI) ui : null;
} catch( Throwable ex ) {
// ignore
return null;
}
}
private static boolean getUIMethodInitialized;
private static MethodHandle getUIMethod;
/** /**
* Returns the preferred font family to be used for (nearly) all fonts; or {@code null}. * Returns the preferred font family to be used for (nearly) all fonts; or {@code null}.
* *

View File

@@ -103,7 +103,10 @@ public interface FlatSystemProperties
* <p> * <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br> * <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code false} (since v2; was {@code true} in v1) * <strong>Default</strong> {@code false} (since v2; was {@code true} in v1)
*
* @deprecated No longer used since FlatLaf 3.3. Retained for API compatibility.
*/ */
@Deprecated
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"; String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
/** /**
@@ -169,19 +172,33 @@ public interface FlatSystemProperties
String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"; String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary";
/** /**
* Specifies a directory in which the native FlatLaf libraries have been extracted. * Specifies a directory in which the FlatLaf native libraries are searched for.
* The path can be absolute or relative to current application working directory. * The path can be absolute or relative to current application working directory.
* This can be used to avoid extraction of the native libraries to the temporary directory at runtime. * This can be used to avoid extraction of the native libraries to the temporary directory at runtime.
* <p> * <p>
* If the value is {@code "system"}, then {@link System#loadLibrary(String)} is * If the value is {@code "system"} (supported since FlatLaf 2.6),
* used to load the native library. * then {@link System#loadLibrary(String)} is used to load the native library.
* Searches for the native library in classloader of caller * This searches for the native library in classloader of caller
* (using {@link ClassLoader#findLibrary(String)}) and in paths specified * (using {@link ClassLoader#findLibrary(String)}) and in paths specified
* in system properties {@code sun.boot.library.path} and {@code java.library.path}. * in system properties {@code sun.boot.library.path} and {@code java.library.path}.
* (supported since FlatLaf 2.6)
* <p> * <p>
* If the native library can not loaded from the given path (or via {@link System#loadLibrary(String)}), * If the native library can not be loaded from the given path (or via {@link System#loadLibrary(String)}),
* then the embedded native library is extracted to the temporary directory and loaded from there. * then the embedded native library is extracted to the temporary directory and loaded from there.
* <p>
* The file names of the native libraries must be either:
* <ul>
* <li>the same as in flatlaf.jar in package 'com/formdev/flatlaf/natives' (required for "system") or
* <li>when downloaded from Maven central then as described here:
* <a href="https://www.formdev.com/flatlaf/native-libraries/">https://www.formdev.com/flatlaf/native-libraries/</a>
* (requires FlatLaf 3.4)
* </ul>
* <p>
* <strong>Note</strong>: Since FlatLaf 3.1 it is recommended to download the
* FlatLaf native libraries from Maven central and distribute them with your
* application in the same directory as flatlaf.jar.
* Then it is <strong>not necessary</strong> to set this system property.
* See <a href="https://www.formdev.com/flatlaf/native-libraries/">https://www.formdev.com/flatlaf/native-libraries/</a>
* for details.
* *
* @since 2 * @since 2
*/ */

View File

@@ -203,7 +203,7 @@ class LinuxFontPolicy
* Gets the default font for KDE from KDE configuration files. * Gets the default font for KDE from KDE configuration files.
* *
* The Swing fonts are not updated when the user changes system font size * The Swing fonts are not updated when the user changes system font size
* (System Settings > Fonts > Force Font DPI). A application restart is necessary. * (System Settings > Fonts > Force Font DPI). An application restart is necessary.
* This is the same behavior as in native KDE applications. * This is the same behavior as in native KDE applications.
* *
* The "display scale factor" (kdeglobals: [KScreen] > ScaleFactor) is not used * The "display scale factor" (kdeglobals: [KScreen] > ScaleFactor) is not used

View File

@@ -172,7 +172,7 @@ debug*/
targetTopY = popupLocation.y; targetTopY = popupLocation.y;
targetBottomY = popupLocation.y + popupSize.height; targetBottomY = popupLocation.y + popupSize.height;
// install own event queue to supress mouse events when mouse is moved within safe triangle // install own event queue to suppress mouse events when mouse is moved within safe triangle
if( subMenuEventQueue == null ) if( subMenuEventQueue == null )
subMenuEventQueue = new SubMenuEventQueue(); subMenuEventQueue = new SubMenuEventQueue();

View File

@@ -90,7 +90,7 @@ public class FlatTabbedPaneCloseIcon
closeSize.width, closeSize.height, closeArc, closeArc ); closeSize.width, closeSize.height, closeArc, closeArc );
} }
// set cross color // set color of cross
Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground ); Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) ); g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );

View File

@@ -57,11 +57,11 @@ public class FlatTreeOpenIcon
double arc = 1.5; double arc = 1.5;
double arc2 = 0.5; double arc2 = 0.5;
path = FlatUIUtils.createPath( false, path = FlatUIUtils.createPath( false,
// bottom-left of opend part // bottom-left of opened part
2,13.5, 2,13.5,
// top-left of opend part // top-left of opened part
FlatUIUtils.ROUNDED, 4.5,7.5, arc, FlatUIUtils.ROUNDED, 4.5,7.5, arc,
// top-right of opend part // top-right of opened part
FlatUIUtils.ROUNDED, 15.5,7.5, arc2, FlatUIUtils.ROUNDED, 15.5,7.5, arc2,
// bottom-right // bottom-right

View File

@@ -71,7 +71,7 @@ public abstract class FlatWindowAbstractIcon
protected void paintBackground( Component c, Graphics2D g ) { protected void paintBackground( Component c, Graphics2D g ) {
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground ); Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
if( background != null ) { if( background != null ) {
// disable antialiasing for background rectangle painting to avoid blury edges when scaled (e.g. at 125% or 175%) // disable antialiasing for background rectangle painting to avoid blurry edges when scaled (e.g. at 125% or 175%)
Object oldHint = g.getRenderingHint( RenderingHints.KEY_ANTIALIASING ); Object oldHint = g.getRenderingHint( RenderingHints.KEY_ANTIALIASING );
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF ); g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );

View File

@@ -28,7 +28,6 @@ import javax.swing.JComboBox;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JSpinner; import javax.swing.JSpinner;
import javax.swing.JViewport;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicBorders; import javax.swing.plaf.basic.BasicBorders;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
@@ -195,8 +194,7 @@ public class FlatBorder
protected boolean isEnabled( Component c ) { protected boolean isEnabled( Component c ) {
if( c instanceof JScrollPane ) { if( c instanceof JScrollPane ) {
// check whether view component is disabled // check whether view component is disabled
JViewport viewport = ((JScrollPane)c).getViewport(); Component view = FlatScrollPaneUI.getView( (JScrollPane) c );
Component view = (viewport != null) ? viewport.getView() : null;
if( view != null && !isEnabled( view ) ) if( view != null && !isEnabled( view ) )
return false; return false;
} }

View File

@@ -721,14 +721,15 @@ public class FlatButtonUI
} }
protected Color getForeground( JComponent c ) { protected Color getForeground( JComponent c ) {
Color fg = c.getForeground();
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c ); boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
// selected state // selected state
if( ((AbstractButton)c).isSelected() ) { if( ((AbstractButton)c).isSelected() ) {
return buttonStateColor( c, return buttonStateColor( c,
toolBarButton toolBarButton
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : c.getForeground()) ? (toolbarSelectedForeground != null ? toolbarSelectedForeground : fg)
: selectedForeground, : (isCustomForeground( fg ) ? fg : selectedForeground),
toolBarButton toolBarButton
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText) ? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText), : (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
@@ -740,7 +741,7 @@ public class FlatButtonUI
// toolbar button // toolbar button
if( toolBarButton ) { if( toolBarButton ) {
return buttonStateColor( c, return buttonStateColor( c,
c.getForeground(), fg,
disabledText, disabledText,
null, null,
toolbarHoverForeground, toolbarHoverForeground,
@@ -751,7 +752,7 @@ public class FlatButtonUI
return buttonStateColor( c, return buttonStateColor( c,
getForegroundBase( c, def ), getForegroundBase( c, def ),
disabledText, disabledText,
isCustomForeground( c.getForeground() ) ? null : (def ? defaultFocusedForeground : focusedForeground), isCustomForeground( fg ) ? null : (def ? defaultFocusedForeground : focusedForeground),
def ? defaultHoverForeground : hoverForeground, def ? defaultHoverForeground : hoverForeground,
def ? defaultPressedForeground : pressedForeground ); def ? defaultPressedForeground : pressedForeground );
} }

View File

@@ -926,7 +926,7 @@ public class FlatComboBoxUI
protected void configurePopup() { protected void configurePopup() {
super.configurePopup(); super.configurePopup();
// make opaque to avoid that background shines thru border (e.g. at 150% scaling) // make opaque to avoid that background shines through border (e.g. at 150% scaling)
setOpaque( true ); setOpaque( true );
// set popup border // set popup border
@@ -944,7 +944,7 @@ public class FlatComboBoxUI
if( popupBackground != null ) if( popupBackground != null )
list.setBackground( popupBackground ); list.setBackground( popupBackground );
// set popup background because it may shine thru when scaled (e.g. at 150%) // set popup background because it may shine through when scaled (e.g. at 150%)
// use non-UIResource to avoid that it is overwritten when making // use non-UIResource to avoid that it is overwritten when making
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview // popup visible (see JPopupMenu.setInvoker()) in theme editor preview
setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) ); setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) );
@@ -1090,7 +1090,7 @@ public class FlatComboBoxUI
} }
// using synchronized to avoid problems with code that modifies combo box // using synchronized to avoid problems with code that modifies combo box
// (model, selection, etc) not on AWT thread (which should be not done) // (model, selection, etc.) not on AWT thread (which should be not done)
synchronized void install( Component c, int focusWidth ) { synchronized void install( Component c, int focusWidth ) {
if( !(c instanceof JComponent) ) if( !(c instanceof JComponent) )
return; return;
@@ -1242,7 +1242,7 @@ public class FlatComboBoxUI
* Key selection manager that delegates to the default manager. * Key selection manager that delegates to the default manager.
* Shows the popup if Space key is pressed and "typed characters" buffer is empty. * Shows the popup if Space key is pressed and "typed characters" buffer is empty.
* If items contain spaces (e.g. "a b") it is still possible to select them * If items contain spaces (e.g. "a b") it is still possible to select them
* by pressing keys a, Space and b. * by pressing keys 'a', 'Space' and 'b'.
*/ */
private class FlatKeySelectionManager private class FlatKeySelectionManager
implements JComboBox.KeySelectionManager, UIResource implements JComboBox.KeySelectionManager, UIResource

View File

@@ -370,7 +370,11 @@ public class FlatFileChooserUI
// get system icon // get system icon
if( f != null ) { if( f != null ) {
icon = getFileChooser().getFileSystemView().getSystemIcon( f ); try {
icon = getFileChooser().getFileSystemView().getSystemIcon( f );
} catch( NullPointerException ex ) {
// Java 21 may throw a NPE for exe files that use default Windows exe icon
}
if( icon != null ) { if( icon != null ) {
if( icon instanceof ImageIcon ) if( icon instanceof ImageIcon )
@@ -540,31 +544,36 @@ public class FlatFileChooserUI
if( doNotUseSystemIcons() ) if( doNotUseSystemIcons() )
return new FlatFileViewDirectoryIcon(); return new FlatFileViewDirectoryIcon();
// Java 17+ supports getting larger system icons
try { try {
if( SystemInfo.isJava_17_orLater ) { // Java 17+ supports getting larger system icons
Method m = fsv.getClass().getMethod( "getSystemIcon", File.class, int.class, int.class ); try {
return (Icon) m.invoke( fsv, file, iconSize.width, iconSize.height ); if( SystemInfo.isJava_17_orLater ) {
} else if( iconSize.width > 16 || iconSize.height > 16 ) { Method m = fsv.getClass().getMethod( "getSystemIcon", File.class, int.class, int.class );
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" ); return (Icon) m.invoke( fsv, file, iconSize.width, iconSize.height );
if( cls.isInstance( file ) ) { } else if( iconSize.width > 16 || iconSize.height > 16 ) {
Method m = file.getClass().getMethod( "getIcon", boolean.class ); Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
m.setAccessible( true ); if( cls.isInstance( file ) ) {
Image image = (Image) m.invoke( file, true ); Method m = file.getClass().getMethod( "getIcon", boolean.class );
if( image != null ) m.setAccessible( true );
return new ImageIcon( image ); Image image = (Image) m.invoke( file, true );
if( image != null )
return new ImageIcon( image );
}
} }
} catch( Exception ex ) {
// do not log InaccessibleObjectException because access
// may be denied via VM option '--illegal-access=deny' (default in Java 16)
// (not catching InaccessibleObjectException here because it is new in Java 9, but FlatLaf also runs on Java 8)
if( !"java.lang.reflect.InaccessibleObjectException".equals( ex.getClass().getName() ) )
LoggingFacade.INSTANCE.logSevere( null, ex );
} }
} catch( Exception ex ) {
// do not log InaccessibleObjectException because access
// may be denied via VM option '--illegal-access=deny' (default in Java 16)
// (not catching InaccessibleObjectException here because it is new in Java 9, but FlatLaf also runs on Java 8)
if( !"java.lang.reflect.InaccessibleObjectException".equals( ex.getClass().getName() ) )
LoggingFacade.INSTANCE.logSevere( null, ex );
}
// get system icon in default size 16x16 // get system icon in default size 16x16
return fsv.getSystemIcon( file ); return fsv.getSystemIcon( file );
} catch( NullPointerException ex ) {
// Java 21 may throw a NPE for exe files that use default Windows exe icon
return new FlatFileViewDirectoryIcon();
}
} }
protected void directoryChanged( File file ) { protected void directoryChanged( File file ) {

View File

@@ -22,6 +22,7 @@ import java.awt.Component;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import javax.swing.JComponent;
/** /**
* Line border for various components. * Line border for various components.
@@ -66,6 +67,9 @@ public class FlatLineBorder
@Override @Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( c instanceof JComponent && ((JComponent)c).getClientProperty( FlatPopupFactory.KEY_POPUP_USES_NATIVE_BORDER ) != null )
return;
Graphics2D g2 = (Graphics2D) g.create(); Graphics2D g2 = (Graphics2D) g.create();
try { try {
FlatUIUtils.setRenderingHints( g2 ); FlatUIUtils.setRenderingHints( g2 );

View File

@@ -411,7 +411,7 @@ public class FlatListUI
int leftIndex = locationToIndex( list, new Point( r.x - 1, r.y ) ); int leftIndex = locationToIndex( list, new Point( r.x - 1, r.y ) );
int rightIndex = locationToIndex( list, new Point( r.x + r.width, r.y ) ); int rightIndex = locationToIndex( list, new Point( r.x + r.width, r.y ) );
// special handling for the case that last column contains less cells than the other columns // special handling for the case that last column contains fewer cells than the other columns
boolean ltr = list.getComponentOrientation().isLeftToRight(); boolean ltr = list.getComponentOrientation().isLeftToRight();
if( !ltr && leftIndex >= 0 && leftIndex != row && leftIndex == locationToIndex( list, new Point( r.x - 1, r.y - 1 ) ) ) if( !ltr && leftIndex >= 0 && leftIndex != row && leftIndex == locationToIndex( list, new Point( r.x - 1, r.y - 1 ) ) )
leftIndex = -1; leftIndex = -1;

View File

@@ -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;
@@ -271,7 +272,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 +322,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;
} }
} }
} }

View File

@@ -78,9 +78,15 @@ class FlatNativeLibrary
// //
// To avoid this, flatlaf.dll is not linked to jawt.dll, // To avoid this, flatlaf.dll is not linked to jawt.dll,
// which avoids loading jawt.dll when flatlaf.dll is loaded. // which avoids loading jawt.dll when flatlaf.dll is loaded.
// Instead flatlaf.dll dynamically loads jawt.dll when first used, // Instead, flatlaf.dll dynamically loads jawt.dll when first used,
// which is guaranteed after AWT initialization. // which is guaranteed after AWT initialization.
} else if( SystemInfo.isMacOS_10_14_Mojave_orLater && (SystemInfo.isAARCH64 || SystemInfo.isX86_64) ) {
// macOS: requires macOS 10.14 or later (arm64 or x86_64)
classifier = SystemInfo.isAARCH64 ? "macos-arm64" : "macos-x86_64";
ext = "dylib";
} else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) { } else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) {
// Linux: requires x86_64 // Linux: requires x86_64
@@ -111,13 +117,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( "Did not find library '" + System.mapLibraryName( libraryName )
+ "' in java.library.path '" + System.getProperty( "java.library.path" )
+ "', using extracted 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( "Did not find library '"
+ libraryFile.getName()
+ (libraryName2 != null ? ("' or '" + libraryName2) : "")
+ "' in '" + libraryFile.getParentFile().getAbsolutePath()
+ "', using extracted library instead", null );
} }
} }
@@ -145,6 +170,33 @@ 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 parent = jarFile.getParentFile();
// 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 native library exists
// in "../bin" directory
if( parent.getName().equalsIgnoreCase( "lib" ) ) {
libraryFile = new File( parent.getParentFile(), "bin/" + libraryName );
if( libraryFile.isFile() )
return libraryFile;
}
// native library not found
return null;
}
private static File getJarFile() {
try { try {
// get location of FlatLaf jar // get location of FlatLaf jar
CodeSource codeSource = FlatNativeLibrary.class.getProtectionDomain().getCodeSource(); CodeSource codeSource = FlatNativeLibrary.class.getProtectionDomain().getCodeSource();
@@ -162,31 +214,19 @@ 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( '.' ) );
return jarBasename
+ (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf")
+ '-' + classifier + '.' + ext;
} }
private static void loadJAWT() { private static void loadJAWT() {

View File

@@ -35,6 +35,12 @@ import com.formdev.flatlaf.util.SystemInfo;
*/ */
class FlatNativeLinuxLibrary class FlatNativeLinuxLibrary
{ {
/**
* Checks whether native library is loaded/available.
* <p>
* <b>Note</b>: It is required to invoke this method before invoking any other
* method of this class. Otherwise, the native library may not be loaded.
*/
static boolean isLoaded() { static boolean isLoaded() {
return SystemInfo.isLinux && FlatNativeLibrary.isLoaded(); return SystemInfo.isLinux && FlatNativeLibrary.isLoaded();
} }

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2023 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Window;
/**
* Native methods for macOS.
* <p>
* <b>Note</b>: This is private API. Do not use!
*
* <h2>Methods that use windows as parameter</h2>
*
* For all methods that accept a {@link java.awt.Window} as parameter,
* the underlying macOS window must be already created,
* otherwise the method fails. You can use following to ensure this:
* <pre>{@code
* if( !window.isDisplayable() )
* window.addNotify();
* }</pre>
* or invoke the method after packing the window. E.g.
* <pre>{@code
* window.pack();
* }</pre>
*
* @author Karl Tauber
* @since 3.3
*/
public class FlatNativeMacLibrary
{
/**
* Checks whether native library is loaded/available.
* <p>
* <b>Note</b>: It is required to invoke this method before invoking any other
* method of this class. Otherwise, the native library may not be loaded.
*/
public static boolean isLoaded() {
return FlatNativeLibrary.isLoaded();
}
public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor );
}

View File

@@ -17,20 +17,26 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window; import java.awt.Window;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.List; import java.util.List;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JRootPane; import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import javax.swing.plaf.BorderUIResource;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatSystemProperties; import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.JBRCustomDecorations.JBRWindowTopBorder; import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
/** /**
@@ -54,27 +60,15 @@ public class FlatNativeWindowBorder
!SystemInfo.isWinPE && !SystemInfo.isWinPE &&
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true ); FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true );
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
private static final boolean canUseJBRCustomDecorations =
canUseWindowDecorations &&
SystemInfo.isJetBrainsJVM_11_orLater &&
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false );
private static Boolean supported; private static Boolean supported;
private static Provider nativeProvider; private static Provider nativeProvider;
public static boolean isSupported() { public static boolean isSupported() {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.isSupported();
initialize(); initialize();
return supported; return supported;
} }
static Object install( JRootPane rootPane ) { static Object install( JRootPane rootPane ) {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.install( rootPane );
if( !isSupported() ) if( !isSupported() )
return null; return null;
@@ -163,11 +157,6 @@ public class FlatNativeWindowBorder
} }
static void uninstall( JRootPane rootPane, Object data ) { static void uninstall( JRootPane rootPane, Object data ) {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.uninstall( rootPane, data );
return;
}
if( !isSupported() ) if( !isSupported() )
return; return;
@@ -215,9 +204,6 @@ public class FlatNativeWindowBorder
} }
public static boolean hasCustomDecoration( Window window ) { public static boolean hasCustomDecoration( Window window ) {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.hasCustomDecoration( window );
if( !isSupported() ) if( !isSupported() )
return false; return false;
@@ -225,11 +211,6 @@ public class FlatNativeWindowBorder
} }
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) { public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setHasCustomDecoration( window, hasCustomDecoration );
return;
}
if( !isSupported() ) if( !isSupported() )
return; return;
@@ -240,11 +221,6 @@ public class FlatNativeWindowBorder
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds, List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds ) Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
{ {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
return;
}
if( !isSupported() ) if( !isSupported() )
return; return;
@@ -253,7 +229,7 @@ public class FlatNativeWindowBorder
} }
static boolean showWindow( Window window, int cmd ) { static boolean showWindow( Window window, int cmd ) {
if( canUseJBRCustomDecorations || !isSupported() ) if( !isSupported() )
return false; return false;
return nativeProvider.showWindow( window, cmd ); return nativeProvider.showWindow( window, cmd );
@@ -320,20 +296,36 @@ public class FlatNativeWindowBorder
* No longer needed since Windows 11. * No longer needed since Windows 11.
*/ */
static class WindowTopBorder static class WindowTopBorder
extends JBRCustomDecorations.JBRWindowTopBorder extends BorderUIResource.EmptyBorderUIResource
{ {
private static WindowTopBorder instance; private static WindowTopBorder instance;
static JBRWindowTopBorder getInstance() { private final Color activeLightColor = new Color( 0x707070 );
if( canUseJBRCustomDecorations ) private final Color activeDarkColor = new Color( 0x2D2E2F );
return JBRWindowTopBorder.getInstance(); private final Color inactiveLightColor = new Color( 0xaaaaaa );
private final Color inactiveDarkColor = new Color( 0x494A4B );
private boolean colorizationAffectsBorders;
private Color activeColor;
static WindowTopBorder getInstance() {
if( instance == null ) if( instance == null )
instance = new WindowTopBorder(); instance = new WindowTopBorder();
return instance; return instance;
} }
@Override WindowTopBorder() {
super( 1, 0, 0, 0 );
update();
installListeners();
}
void update() {
colorizationAffectsBorders = isColorizationColorAffectsBorders();
activeColor = calculateActiveBorderColor();
}
void installListeners() { void installListeners() {
nativeProvider.addChangeListener( e -> { nativeProvider.addChangeListener( e -> {
update(); update();
@@ -346,19 +338,69 @@ public class FlatNativeWindowBorder
} ); } );
} }
@Override
boolean isColorizationColorAffectsBorders() { boolean isColorizationColorAffectsBorders() {
return nativeProvider.isColorizationColorAffectsBorders(); return nativeProvider.isColorizationColorAffectsBorders();
} }
@Override
Color getColorizationColor() { Color getColorizationColor() {
return nativeProvider.getColorizationColor(); return nativeProvider.getColorizationColor();
} }
@Override
int getColorizationColorBalance() { int getColorizationColorBalance() {
return nativeProvider.getColorizationColorBalance(); return nativeProvider.getColorizationColorBalance();
} }
private Color calculateActiveBorderColor() {
if( !colorizationAffectsBorders )
return null;
Color colorizationColor = getColorizationColor();
if( colorizationColor != null ) {
int colorizationColorBalance = getColorizationColorBalance();
if( colorizationColorBalance < 0 || colorizationColorBalance > 100 )
colorizationColorBalance = 100;
if( colorizationColorBalance == 0 )
return new Color( 0xD9D9D9 );
if( colorizationColorBalance == 100 )
return colorizationColor;
float alpha = colorizationColorBalance / 100.0f;
float remainder = 1 - alpha;
int r = Math.round( colorizationColor.getRed() * alpha + 0xD9 * remainder );
int g = Math.round( colorizationColor.getGreen() * alpha + 0xD9 * remainder );
int b = Math.round( colorizationColor.getBlue() * alpha + 0xD9 * remainder );
// avoid potential IllegalArgumentException in Color constructor
r = Math.min( Math.max( r, 0 ), 255 );
g = Math.min( Math.max( g, 0 ), 255 );
b = Math.min( Math.max( b, 0 ), 255 );
return new Color( r, g, b );
}
Color activeBorderColor = (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.frame.activeBorderColor" );
return (activeBorderColor != null) ? activeBorderColor : UIManager.getColor( "MenuBar.borderColor" );
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Window window = SwingUtilities.windowForComponent( c );
boolean active = window != null && window.isActive();
boolean dark = FlatLaf.isLafDark();
g.setColor( active
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
: (dark ? inactiveDarkColor : inactiveLightColor) );
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
}
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
g.fillRect( x, y, width, 1 );
}
void repaintBorder( Component c ) {
c.repaint( 0, 0, c.getWidth(), 1 );
}
} }
} }

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Window; import java.awt.Window;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
@@ -31,6 +32,12 @@ public class FlatNativeWindowsLibrary
{ {
private static long osBuildNumber = Long.MIN_VALUE; private static long osBuildNumber = Long.MIN_VALUE;
/**
* Checks whether native library is loaded/available.
* <p>
* <b>Note</b>: It is required to invoke this method before invoking any other
* method of this class. Otherwise, the native library may not be loaded.
*/
public static boolean isLoaded() { public static boolean isLoaded() {
return SystemInfo.isWindows && FlatNativeLibrary.isLoaded(); return SystemInfo.isWindows && FlatNativeLibrary.isLoaded();
} }
@@ -93,15 +100,60 @@ public class FlatNativeWindowsLibrary
public native static boolean setWindowCornerPreference( long hwnd, int cornerPreference ); public native static boolean setWindowCornerPreference( long hwnd, int cornerPreference );
/** /**
* Sets the color of the window border. * DWMWINDOWATTRIBUTE
* The red/green/blue values must be in range {@code 0 - 255}. * see https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
* If red is {@code -1}, then the system default border color is used (useful to reset the border color). *
* If red is {@code -2}, then no border is painted. * @since 3.3
* <p> */
* Invokes Win32 API method {@code DwmSetWindowAttribute(DWMWA_BORDER_COLOR)}. public static final int
DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
DWMWA_BORDER_COLOR = 34,
DWMWA_CAPTION_COLOR = 35,
DWMWA_TEXT_COLOR = 36;
/**
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code BOOL} attribute value.
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
*
* @since 3.3
*/
public native static boolean dwmSetWindowAttributeBOOL( long hwnd, int attribute, boolean value );
/**
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code DWORD} attribute value.
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
*
* @since 3.3
*/
public native static boolean dwmSetWindowAttributeDWORD( long hwnd, int attribute, int value );
/** @since 3.3 */
public static final int
// use this constant to reset any window part colors to the system default behavior
DWMWA_COLOR_DEFAULT = 0xFFFFFFFF,
// use this constant to specify that a window part should not be rendered
DWMWA_COLOR_NONE = 0xFFFFFFFE;
/** @since 3.3 */
public static final Color COLOR_NONE = new Color( 0, true );
/**
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code COLORREF} attribute value.
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute * See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
* <p> * <p>
* Supported since Windows 11 Build 22000. * Supported since Windows 11 Build 22000.
*
* @since 3.3
*/ */
public native static boolean setWindowBorderColor( long hwnd, int red, int green, int blue ); public static boolean dwmSetWindowAttributeCOLORREF( long hwnd, int attribute, Color color ) {
// convert color to Windows RGB value
int rgb = (color == COLOR_NONE)
? DWMWA_COLOR_NONE
: (color != null
? (color.getRed() | (color.getGreen() << 8) | (color.getBlue() << 16))
: DWMWA_COLOR_DEFAULT);
// DwmSetWindowAttribute() expects COLORREF as attribute value, which is defined as DWORD
return dwmSetWindowAttributeDWORD( hwnd, attribute, rgb );
}
} }

View File

@@ -30,9 +30,7 @@ import javax.swing.JPanel;
import javax.swing.JRootPane; import javax.swing.JRootPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicHTML; import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicOptionPaneUI; import javax.swing.plaf.basic.BasicOptionPaneUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
@@ -115,13 +113,6 @@ public class FlatOptionPaneUI
sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true ); sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true );
} }
@Override
protected void installComponents() {
super.installComponents();
updateChildPanels( optionPane );
}
@Override @Override
protected PropertyChangeListener createPropertyChangeListener() { protected PropertyChangeListener createPropertyChangeListener() {
PropertyChangeListener superListener = super.createPropertyChangeListener(); PropertyChangeListener superListener = super.createPropertyChangeListener();
@@ -155,6 +146,13 @@ public class FlatOptionPaneUI
protected Container createMessageArea() { protected Container createMessageArea() {
Container messageArea = super.createMessageArea(); Container messageArea = super.createMessageArea();
// use non-UIResource OptionPane.messageAreaBorder to avoid that it is replaced when switching LaF
// and make panel non-opaque for OptionPane.background
updateAreaPanel( messageArea );
// make known sub-panels non-opaque for OptionPane.background
updateKnownChildPanels( messageArea );
// set icon-message gap // set icon-message gap
if( iconMessageGap > 0 ) { if( iconMessageGap > 0 ) {
Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" ); Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" );
@@ -169,6 +167,10 @@ public class FlatOptionPaneUI
protected Container createButtonArea() { protected Container createButtonArea() {
Container buttonArea = super.createButtonArea(); Container buttonArea = super.createButtonArea();
// use non-UIResource OptionPane.buttonAreaBorder to avoid that it is replaced when switching LaF
// and make panel non-opaque for OptionPane.background
updateAreaPanel( buttonArea );
// scale button padding and subtract focusWidth // scale button padding and subtract focusWidth
if( buttonArea.getLayout() instanceof ButtonAreaLayout ) { if( buttonArea.getLayout() instanceof ButtonAreaLayout ) {
ButtonAreaLayout layout = (ButtonAreaLayout) buttonArea.getLayout(); ButtonAreaLayout layout = (ButtonAreaLayout) buttonArea.getLayout();
@@ -218,22 +220,33 @@ public class FlatOptionPaneUI
super.addMessageComponents( container, cons, msg, maxll, internallyCreated ); super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
} }
private void updateChildPanels( Container c ) { private void updateAreaPanel( Container area ) {
if( !(area instanceof JPanel) )
return;
// use non-UIResource border to avoid that it is replaced when switching LaF
// and make panel non-opaque for OptionPane.background
JPanel panel = (JPanel) area;
panel.setBorder( FlatUIUtils.nonUIResource( panel.getBorder() ) );
panel.setOpaque( false );
}
private void updateKnownChildPanels( Container c ) {
for( Component child : c.getComponents() ) { for( Component child : c.getComponents() ) {
if( child.getClass() == JPanel.class ) { if( child instanceof JPanel && child.getName() != null ) {
JPanel panel = (JPanel)child; switch( child.getName() ) {
case "OptionPane.realBody":
// make sub-panel non-opaque for OptionPane.background case "OptionPane.body":
panel.setOpaque( false ); case "OptionPane.separator":
case "OptionPane.break":
// use non-UIResource borders to avoid that they are replaced when switching LaF // make known sub-panels non-opaque for OptionPane.background
Border border = panel.getBorder(); ((JPanel)child).setOpaque( false );
if( border instanceof UIResource ) break;
panel.setBorder( FlatUIUtils.nonUIResource( border ) ); }
} }
if( child instanceof Container ) if( child instanceof Container )
updateChildPanels( (Container) child ); updateKnownChildPanels( (Container) child );
} }
} }

View File

@@ -71,6 +71,8 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatPopupFactory public class FlatPopupFactory
extends PopupFactory extends PopupFactory
{ {
static final String KEY_POPUP_USES_NATIVE_BORDER = "FlatLaf.internal.FlatPopupFactory.popupUsesNativeBorder";
private MethodHandle java8getPopupMethod; private MethodHandle java8getPopupMethod;
private MethodHandle java9getPopupMethod; private MethodHandle java9getPopupMethod;
@@ -92,17 +94,20 @@ public class FlatPopupFactory
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents ); return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
// macOS and Linux adds drop shadow to heavy weight popups // macOS and Linux adds drop shadow to heavy weight popups
if( SystemInfo.isMacOS || SystemInfo.isLinux ) if( SystemInfo.isMacOS || SystemInfo.isLinux ) {
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents ); NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
if( popup.popupWindow != null && SystemInfo.isMacOS && FlatNativeMacLibrary.isLoaded() )
setupRoundedBorder( popup.popupWindow, owner, contents );
return popup;
}
// Windows 11 with FlatLaf native library can use rounded corners and shows drop shadow for heavy weight popups // Windows 11 with FlatLaf native library can use rounded corners and shows drop shadow for heavy weight popups
int borderCornerRadius;
if( isWindows11BorderSupported() && if( isWindows11BorderSupported() &&
(borderCornerRadius = getBorderCornerRadius( owner, contents )) > 0 ) getBorderCornerRadius( owner, contents ) > 0 )
{ {
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents ); NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
if( popup.popupWindow != null ) if( popup.popupWindow != null )
setupWindows11Border( popup.popupWindow, contents, borderCornerRadius ); setupRoundedBorder( popup.popupWindow, owner, contents );
return popup; return popup;
} }
@@ -162,67 +167,6 @@ public class FlatPopupFactory
} }
} }
/**
* Shows the given popup and, if necessary, fixes the location of a heavy weight popup window.
* <p>
* On a dual screen setup, where screens use different scale factors, it may happen
* that the window location changes when showing a heavy weight popup window.
* E.g. when opening a dialog on the secondary screen and making combobox popup visible.
* <p>
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
*/
private static void showPopupAndFixLocation( Popup popup, Window popupWindow ) {
if( popupWindow != null ) {
// remember location of heavy weight popup window
int x = popupWindow.getX();
int y = popupWindow.getY();
popup.show();
// restore popup window location if it has changed
// (probably scaled when screens use different scale factors)
if( popupWindow.getX() != x || popupWindow.getY() != y )
popupWindow.setLocation( x, y );
} else
popup.show();
}
private boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
Object value = getOption( owner, contents, clientKey, uiKey );
return (value instanceof Boolean) ? (Boolean) value : false;
}
private int getBorderCornerRadius( Component owner, Component contents ) {
String uiKey =
(contents instanceof BasicComboPopup) ? "ComboBox.borderCornerRadius" :
(contents instanceof JPopupMenu) ? "PopupMenu.borderCornerRadius" :
(contents instanceof JToolTip) ? "ToolTip.borderCornerRadius" :
"Popup.borderCornerRadius";
Object value = getOption( owner, contents, FlatClientProperties.POPUP_BORDER_CORNER_RADIUS, uiKey );
return (value instanceof Integer) ? (Integer) value : 0;
}
/**
* Get option from:
* <ol>
* <li>client property {@code clientKey} of {@code owner}
* <li>client property {@code clientKey} of {@code contents}
* <li>UI property {@code uiKey}
* </ol>
*/
private Object getOption( Component owner, Component contents, String clientKey, String uiKey ) {
for( Component c : new Component[] { owner, contents } ) {
if( c instanceof JComponent ) {
Object value = ((JComponent)c).getClientProperty( clientKey );
if( value != null )
return value;
}
}
return UIManager.get( uiKey );
}
/** /**
* There is no API in Java 8 to force creation of heavy weight popups, * There is no API in Java 8 to force creation of heavy weight popups,
* but it is possible with reflection. Java 9 provides a new method. * but it is possible with reflection. Java 9 provides a new method.
@@ -258,6 +202,33 @@ public class FlatPopupFactory
} }
} }
private static boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
Object value = getOption( owner, contents, clientKey, uiKey );
return (value instanceof Boolean) ? (Boolean) value : false;
}
/**
* Get option from:
* <ol>
* <li>client property {@code clientKey} of {@code owner}
* <li>client property {@code clientKey} of {@code contents}
* <li>UI property {@code uiKey}
* </ol>
*/
private static Object getOption( Component owner, Component contents, String clientKey, String uiKey ) {
for( Component c : new Component[] { owner, contents } ) {
if( c instanceof JComponent ) {
Object value = ((JComponent)c).getClientProperty( clientKey );
if( value != null )
return value;
}
}
return UIManager.get( uiKey );
}
//---- tooltips -----------------------------------------------------------
/** /**
* Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20). * Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20).
* In case that the tooltip would be partly outside of the screen, * In case that the tooltip would be partly outside of the screen,
@@ -342,48 +313,58 @@ public class FlatPopupFactory
((JComponent)owner).getToolTipLocation( me ) != null; ((JComponent)owner).getToolTipLocation( me ) != null;
} }
//---- native rounded border ----------------------------------------------
private static boolean isWindows11BorderSupported() { private static boolean isWindows11BorderSupported() {
return SystemInfo.isWindows_11_orLater && FlatNativeWindowsLibrary.isLoaded(); return SystemInfo.isWindows_11_orLater && FlatNativeWindowsLibrary.isLoaded();
} }
private static void setupWindows11Border( Window popupWindow, Component contents, int borderCornerRadius ) { private static void setupRoundedBorder( Window popupWindow, Component owner, Component contents ) {
// make sure that the Windows 11 window is created // make sure that the native window is created
if( !popupWindow.isDisplayable() ) if( !popupWindow.isDisplayable() )
popupWindow.addNotify(); popupWindow.addNotify();
// get window handle int borderCornerRadius = getBorderCornerRadius( owner, contents );
long hwnd = FlatNativeWindowsLibrary.getHWND( popupWindow ); float borderWidth = getRoundedBorderWidth( owner, contents );
// set corner preference // get Swing border color
int cornerPreference = (borderCornerRadius <= 4) Color borderColor = null; // use system default color
? FlatNativeWindowsLibrary.DWMWCP_ROUNDSMALL // 4px
: FlatNativeWindowsLibrary.DWMWCP_ROUND; // 8px
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, cornerPreference );
// set border color
int red = -1; // use system default color
int green = 0;
int blue = 0;
if( contents instanceof JComponent ) { if( contents instanceof JComponent ) {
Border border = ((JComponent)contents).getBorder(); Border border = ((JComponent)contents).getBorder();
border = FlatUIUtils.unwrapNonUIResourceBorder( border ); border = FlatUIUtils.unwrapNonUIResourceBorder( border );
// get color from border of contents (e.g. JPopupMenu or JToolTip) // get color from border of contents (e.g. JPopupMenu or JToolTip)
Color borderColor = null;
if( border instanceof FlatLineBorder ) if( border instanceof FlatLineBorder )
borderColor = ((FlatLineBorder)border).getLineColor(); borderColor = ((FlatLineBorder)border).getLineColor();
else if( border instanceof LineBorder ) else if( border instanceof LineBorder )
borderColor = ((LineBorder)border).getLineColor(); borderColor = ((LineBorder)border).getLineColor();
else if( border instanceof EmptyBorder ) else if( border instanceof EmptyBorder )
red = -2; // do not paint border borderColor = FlatNativeWindowsLibrary.COLOR_NONE; // do not paint border
if( borderColor != null ) { // avoid that FlatLineBorder paints the Swing border
red = borderColor.getRed(); ((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, true );
green = borderColor.getGreen(); }
blue = borderColor.getBlue();
} if( SystemInfo.isWindows ) {
// get native window handle
long hwnd = FlatNativeWindowsLibrary.getHWND( popupWindow );
// set corner preference
int cornerPreference = (borderCornerRadius <= 4)
? FlatNativeWindowsLibrary.DWMWCP_ROUNDSMALL // 4px
: FlatNativeWindowsLibrary.DWMWCP_ROUND; // 8px
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, cornerPreference );
// set border color
FlatNativeWindowsLibrary.dwmSetWindowAttributeCOLORREF( hwnd, FlatNativeWindowsLibrary.DWMWA_BORDER_COLOR, borderColor );
} else if( SystemInfo.isMacOS ) {
if( borderColor == null || borderColor == FlatNativeWindowsLibrary.COLOR_NONE )
borderWidth = 0;
// set corner radius, border width and color
FlatNativeMacLibrary.setWindowRoundedBorder( popupWindow, borderCornerRadius,
borderWidth, (borderColor != null) ? borderColor.getRGB() : 0 );
} }
FlatNativeWindowsLibrary.setWindowBorderColor( hwnd, red, green, blue );
} }
private static void resetWindows11Border( Window popupWindow ) { private static void resetWindows11Border( Window popupWindow ) {
@@ -396,7 +377,34 @@ public class FlatPopupFactory
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, FlatNativeWindowsLibrary.DWMWCP_DONOTROUND ); FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, FlatNativeWindowsLibrary.DWMWCP_DONOTROUND );
} }
private static int getBorderCornerRadius( Component owner, Component contents ) {
String uiKey =
(contents instanceof BasicComboPopup) ? "ComboBox.borderCornerRadius" :
(contents instanceof JPopupMenu) ? "PopupMenu.borderCornerRadius" :
(contents instanceof JToolTip) ? "ToolTip.borderCornerRadius" :
"Popup.borderCornerRadius";
Object value = getOption( owner, contents, FlatClientProperties.POPUP_BORDER_CORNER_RADIUS, uiKey );
return (value instanceof Integer) ? (Integer) value : 0;
}
private static float getRoundedBorderWidth( Component owner, Component contents ) {
String uiKey =
(contents instanceof BasicComboPopup) ? "ComboBox.roundedBorderWidth" :
(contents instanceof JPopupMenu) ? "PopupMenu.roundedBorderWidth" :
(contents instanceof JToolTip) ? "ToolTip.roundedBorderWidth" :
"Popup.roundedBorderWidth";
Object value = getOption( owner, contents, FlatClientProperties.POPUP_ROUNDED_BORDER_WIDTH, uiKey );
return (value instanceof Number) ? ((Number)value).floatValue() : 0;
}
//---- fixes --------------------------------------------------------------
private static boolean overlapsHeavyWeightComponent( Component owner, Component contents, int x, int y ) { private static boolean overlapsHeavyWeightComponent( Component owner, Component contents, int x, int y ) {
if( owner == null )
return false;
Window window = SwingUtilities.getWindowAncestor( owner ); Window window = SwingUtilities.getWindowAncestor( owner );
if( window == null ) if( window == null )
return false; return false;
@@ -427,7 +435,7 @@ public class FlatPopupFactory
* On Linux with Wayland, since Java 21, Swing adds a window focus listener to popup owner/invoker window, * On Linux with Wayland, since Java 21, Swing adds a window focus listener to popup owner/invoker window,
* which hides the popup as soon as the owner/invoker window looses focus. * which hides the popup as soon as the owner/invoker window looses focus.
* This works fine for light-weight popups. * This works fine for light-weight popups.
* It also works for heavy-weight popups if the do not request focus. * It also works for heavy-weight popups if they do not request focus.
* Because FlatLaf always uses heavy-weight popups, all popups that request focus * Because FlatLaf always uses heavy-weight popups, all popups that request focus
* are broken since Java 21. * are broken since Java 21.
* *
@@ -438,7 +446,7 @@ public class FlatPopupFactory
*/ */
private static void fixLinuxWaylandJava21focusIssue( Component owner ) { private static void fixLinuxWaylandJava21focusIssue( Component owner ) {
// only necessary on Linux when running in Java 21+ // only necessary on Linux when running in Java 21+
if( !SystemInfo.isLinux || SystemInfo.javaVersion < SystemInfo.toVersion( 21, 0, 0, 0 ) ) if( owner == null || !SystemInfo.isLinux || SystemInfo.javaVersion < SystemInfo.toVersion( 21, 0, 0, 0 ) )
return; return;
// get window // get window
@@ -455,6 +463,31 @@ public class FlatPopupFactory
} }
} }
/**
* Shows the given popup and, if necessary, fixes the location of a heavy weight popup window.
* <p>
* On a dual screen setup, where screens use different scale factors, it may happen
* that the window location changes when showing a heavy weight popup window.
* E.g. when opening a dialog on the secondary screen and making combobox popup visible.
* <p>
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
*/
private static void showPopupAndFixLocation( Popup popup, Window popupWindow ) {
if( popupWindow != null ) {
// remember location of heavy weight popup window
int x = popupWindow.getX();
int y = popupWindow.getY();
popup.show();
// restore popup window location if it has changed
// (probably scaled when screens use different scale factors)
if( popupWindow.getX() != x || popupWindow.getY() != y )
popupWindow.setLocation( x, y );
} else
popup.show();
}
//---- class NonFlashingPopup --------------------------------------------- //---- class NonFlashingPopup ---------------------------------------------
private static class NonFlashingPopup private static class NonFlashingPopup
@@ -508,6 +541,9 @@ public class FlatPopupFactory
@Override @Override
public void hide() { public void hide() {
if( contents instanceof JComponent )
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, null );
if( delegate != null ) { if( delegate != null ) {
delegate.hide(); delegate.hide();
delegate = null; delegate = null;

View File

@@ -263,7 +263,7 @@ public class FlatRadioButtonUI
@Override @Override
public void paint( Graphics g, JComponent c ) { public void paint( Graphics g, JComponent c ) {
// fill background even if not opaque if // fill background even if not opaque and if:
// - contentAreaFilled is true and // - contentAreaFilled is true and
// - if background color is different to default background color // - if background color is different to default background color
// (this paints selection if using the component as cell renderer) // (this paints selection if using the component as cell renderer)

View File

@@ -50,7 +50,6 @@ 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;
@@ -156,10 +155,6 @@ 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" ) );
} }
// enable dark window appearance on macOS when running in JetBrains Runtime
if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater )
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
} }
@Override @Override
@@ -499,7 +494,7 @@ public class FlatRootPaneUI
@Override @Override
public void invalidateLayout( Container parent ) { public void invalidateLayout( Container parent ) {
if( titlePane != null ) if( titlePane != null )
titlePane.menuBarChanged(); titlePane.menuBarInvalidate();
} }
@Override @Override

View File

@@ -245,7 +245,7 @@ public class FlatScrollBarUI
// because scroll bars do not receive mouse exited event. // because scroll bars do not receive mouse exited event.
// The scroll pane, including its scroll bars, is not part // The scroll pane, including its scroll bars, is not part
// of the component hierarchy and does not receive mouse events // of the component hierarchy and does not receive mouse events
// directly. Instead LWComponentPeer receives mouse events // directly. Instead, LWComponentPeer receives mouse events
// and delegates them to peers, but entered/exited events // and delegates them to peers, but entered/exited events
// are sent only for the whole scroll pane. // are sent only for the whole scroll pane.
// Exited event is only sent when mouse leaves scroll pane. // Exited event is only sent when mouse leaves scroll pane.

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2023 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Component;
import java.awt.Insets;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.util.UIScale;
/**
* Border for {@link javax.swing.JScrollPane}.
*
* @uiDefault ScrollPane.arc int
* @uiDefault ScrollPane.List.arc int
* @uiDefault ScrollPane.Table.arc int
* @uiDefault ScrollPane.TextComponent.arc int
* @uiDefault ScrollPane.Tree.arc int
* @author Karl Tauber
* @since 3.3
*/
public class FlatScrollPaneBorder
extends FlatBorder
{
@Styleable protected int arc = UIManager.getInt( "ScrollPane.arc" );
private boolean isArcStyled;
private final int listArc = FlatUIUtils.getUIInt( "ScrollPane.List.arc", -1 );
private final int tableArc = FlatUIUtils.getUIInt( "ScrollPane.Table.arc", -1 );
private final int textComponentArc = FlatUIUtils.getUIInt( "ScrollPane.TextComponent.arc", -1 );
private final int treeArc = FlatUIUtils.getUIInt( "ScrollPane.Tree.arc", -1 );
@Override
public Object applyStyleProperty( String key, Object value ) {
Object oldValue = super.applyStyleProperty( key, value );
if( "arc".equals( key ) )
isArcStyled = true;
return oldValue;
}
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
insets = super.getBorderInsets( c, insets );
// if view is rounded, increase left and right insets to avoid that the viewport
// is painted over the rounded border on the corners
int padding = getLeftRightPadding( c );
if( padding > 0 ) {
insets.left += padding;
insets.right += padding;
}
return insets;
}
@Override
protected int getArc( Component c ) {
if( isCellEditor( c ) )
return 0;
if( isArcStyled )
return arc;
if( c instanceof JScrollPane ) {
Component view = FlatScrollPaneUI.getView( (JScrollPane) c );
if( listArc >= 0 && view instanceof JList )
return listArc;
if( tableArc >= 0 && view instanceof JTable )
return tableArc;
if( textComponentArc >= 0&& view instanceof JTextComponent )
return textComponentArc;
if( treeArc >= 0 && view instanceof JTree )
return treeArc;
}
return arc;
}
/**
* Returns the scaled left/right padding used when arc is larger than zero.
* <p>
* This is the distance from the inside of the left border to the left side of the view component.
* On the right side, this is the distance between the right side of the view component and
* the vertical scrollbar. Or the inside of the right border if the scrollbar is hidden.
*/
public int getLeftRightPadding( Component c ) {
// Subtract lineWidth from radius because radius is given for the outside
// of the painted line, but insets from super already include lineWidth.
// Reduce padding by 10% to make padding slightly smaller because it is not recognizable
// when the view is minimally painted over the beginning of the border curve.
int arc = getArc( c );
return (arc > 0)
? Math.max( Math.round( UIScale.scale( ((arc / 2f) - getLineWidth( c )) * 0.9f ) ), 0 )
: 0;
}
}

View File

@@ -17,9 +17,12 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Component; import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.awt.KeyboardFocusManager; import java.awt.KeyboardFocusManager;
import java.awt.LayoutManager;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.event.ContainerEvent; import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener; import java.awt.event.ContainerListener;
@@ -41,16 +44,19 @@ import javax.swing.JTree;
import javax.swing.JViewport; import javax.swing.JViewport;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.ScrollPaneConstants; import javax.swing.ScrollPaneConstants;
import javax.swing.ScrollPaneLayout;
import javax.swing.Scrollable; import javax.swing.Scrollable;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollPaneUI; import javax.swing.plaf.basic.BasicScrollPaneUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}. * Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
@@ -97,7 +103,13 @@ public class FlatScrollPaneUI
super.installUI( c ); super.installUI( c );
int focusWidth = UIManager.getInt( "Component.focusWidth" ); int focusWidth = UIManager.getInt( "Component.focusWidth" );
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 ); int arc = UIManager.getInt( "ScrollPane.arc" );
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 && arc == 0 );
// install layout manager
LayoutManager layout = c.getLayout();
if( layout != null && layout.getClass() == ScrollPaneLayout.UIResource.class )
c.setLayout( createScrollPaneLayout() );
installStyle(); installStyle();
@@ -108,6 +120,10 @@ public class FlatScrollPaneUI
public void uninstallUI( JComponent c ) { public void uninstallUI( JComponent c ) {
MigLayoutVisualPadding.uninstall( scrollpane ); MigLayoutVisualPadding.uninstall( scrollpane );
// uninstall layout manager
if( c.getLayout() instanceof FlatScrollPaneLayout )
c.setLayout( new ScrollPaneLayout.UIResource() );
super.uninstallUI( c ); super.uninstallUI( c );
oldStyleValues = null; oldStyleValues = null;
@@ -130,6 +146,13 @@ public class FlatScrollPaneUI
handler = null; handler = null;
} }
/**
* @since 3.3
*/
protected FlatScrollPaneLayout createScrollPaneLayout() {
return new FlatScrollPaneLayout();
}
@Override @Override
protected MouseWheelListener createMouseWheelListener() { protected MouseWheelListener createMouseWheelListener() {
MouseWheelListener superListener = super.createMouseWheelListener(); MouseWheelListener superListener = super.createMouseWheelListener();
@@ -290,8 +313,7 @@ public class FlatScrollPaneUI
Object corner = e.getNewValue(); Object corner = e.getNewValue();
if( corner instanceof JButton && if( corner instanceof JButton &&
((JButton)corner).getBorder() instanceof FlatButtonBorder && ((JButton)corner).getBorder() instanceof FlatButtonBorder &&
scrollpane.getViewport() != null && getView( scrollpane ) instanceof JTable )
scrollpane.getViewport().getView() instanceof JTable )
{ {
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() ); ((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
((JButton)corner).setFocusable( false ); ((JButton)corner).setFocusable( false );
@@ -308,6 +330,18 @@ public class FlatScrollPaneUI
scrollpane.revalidate(); scrollpane.revalidate();
scrollpane.repaint(); scrollpane.repaint();
break; break;
case "border":
Object newBorder = e.getNewValue();
if( newBorder != null && newBorder == UIManager.getBorder( "Table.scrollPaneBorder" ) ) {
// JTable.configureEnclosingScrollPaneUI() replaces the scrollpane border
// with another one --> re-apply style on new border
borderShared = null;
installStyle();
scrollpane.revalidate();
scrollpane.repaint();
}
break;
} }
}; };
} }
@@ -334,9 +368,10 @@ public class FlatScrollPaneUI
/** @since 2 */ /** @since 2 */
protected Object applyStyleProperty( String key, Object value ) { protected Object applyStyleProperty( String key, Object value ) {
if( key.equals( "focusWidth" ) ) { if( key.equals( "focusWidth" ) || key.equals( "arc" ) ) {
int focusWidth = (value instanceof Integer) ? (int) value : UIManager.getInt( "Component.focusWidth" ); int focusWidth = (value instanceof Integer) ? (int) value : UIManager.getInt( "Component.focusWidth" );
LookAndFeel.installProperty( scrollpane, "opaque", focusWidth == 0 ); int arc = (value instanceof Integer) ? (int) value : UIManager.getInt( "ScrollPane.arc" );
LookAndFeel.installProperty( scrollpane, "opaque", focusWidth == 0 && arc == 0 );
} }
if( borderShared == null ) if( borderShared == null )
@@ -402,13 +437,46 @@ public class FlatScrollPaneUI
c.getHeight() - insets.top - insets.bottom ); c.getHeight() - insets.top - insets.bottom );
} }
// if view is rounded, paint rounded background with view background color
// to ensure that free areas at left and right have same color as view
Component view;
float arc = getBorderArc( scrollpane );
if( arc > 0 && (view = getView( scrollpane )) != null ) {
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
g.setColor( view.getBackground() );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
paint( g, c ); paint( g, c );
} }
@Override
public void paint( Graphics g, JComponent c ) {
Border viewportBorder = scrollpane.getViewportBorder();
if( viewportBorder != null ) {
Rectangle r = scrollpane.getViewportBorderBounds();
int padding = getBorderLeftRightPadding( scrollpane );
JScrollBar vsb = scrollpane.getVerticalScrollBar();
if( padding > 0 &&
vsb != null && vsb.isVisible() &&
scrollpane.getLayout() instanceof FlatScrollPaneLayout &&
((FlatScrollPaneLayout)scrollpane.getLayout()).canIncreaseViewportWidth( scrollpane ) )
{
boolean ltr = scrollpane.getComponentOrientation().isLeftToRight();
int extraWidth = Math.min( padding, vsb.getWidth() );
viewportBorder.paintBorder( scrollpane, g, r.x - (ltr ? 0 : extraWidth), r.y, r.width + extraWidth, r.height );
} else
viewportBorder.paintBorder( scrollpane, g, r.x, r.y, r.width, r.height );
}
}
/** @since 1.3 */ /** @since 1.3 */
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) { public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
JViewport viewport = scrollPane.getViewport(); Component view = getView( scrollPane );
Component view = (viewport != null) ? viewport.getView() : null;
if( view == null ) if( view == null )
return false; return false;
@@ -428,6 +496,25 @@ public class FlatScrollPaneUI
return false; return false;
} }
static Component getView( JScrollPane scrollPane ) {
JViewport viewport = scrollPane.getViewport();
return (viewport != null) ? viewport.getView() : null;
}
private static float getBorderArc( JScrollPane scrollPane ) {
Border border = scrollPane.getBorder();
return (border instanceof FlatScrollPaneBorder)
? UIScale.scale( (float) ((FlatScrollPaneBorder)border).getArc( scrollPane ) )
: 0;
}
private static int getBorderLeftRightPadding( JScrollPane scrollPane ) {
Border border = scrollPane.getBorder();
return (border instanceof FlatScrollPaneBorder)
? ((FlatScrollPaneBorder)border).getLeftRightPadding( scrollPane )
: 0;
}
//---- class Handler ------------------------------------------------------ //---- class Handler ------------------------------------------------------
/** /**
@@ -450,13 +537,71 @@ public class FlatScrollPaneUI
@Override @Override
public void focusGained( FocusEvent e ) { public void focusGained( FocusEvent e ) {
// necessary to update focus border // necessary to update focus border
scrollpane.repaint(); if( scrollpane.getBorder() instanceof FlatBorder )
scrollpane.repaint();
} }
@Override @Override
public void focusLost( FocusEvent e ) { public void focusLost( FocusEvent e ) {
// necessary to update focus border // necessary to update focus border
scrollpane.repaint(); if( scrollpane.getBorder() instanceof FlatBorder )
scrollpane.repaint();
}
}
//---- class FlatScrollPaneLayout -----------------------------------------
/**
* @since 3.3
*/
protected static class FlatScrollPaneLayout
extends ScrollPaneLayout.UIResource
{
@Override
public void layoutContainer( Container parent ) {
super.layoutContainer( parent );
JScrollPane scrollPane = (JScrollPane) parent;
int padding = getBorderLeftRightPadding( scrollPane );
if( padding > 0 && vsb != null && vsb.isVisible() ) {
// move vertical scrollbar to trailing edge
Insets insets = scrollPane.getInsets();
Rectangle r = vsb.getBounds();
int y = Math.max( r.y, insets.top + padding );
int y2 = Math.min( r.y + r.height, scrollPane.getHeight() - insets.bottom - padding );
boolean ltr = scrollPane.getComponentOrientation().isLeftToRight();
vsb.setBounds( r.x + (ltr ? padding : -padding), y, r.width, y2 - y );
// increase width of viewport, column header and horizontal scrollbar
if( canIncreaseViewportWidth( scrollPane ) ) {
int extraWidth = Math.min( padding, vsb.getWidth() );
resizeViewport( viewport, extraWidth, ltr );
resizeViewport( colHead, extraWidth, ltr );
resizeViewport( hsb, extraWidth, ltr );
}
}
}
boolean canIncreaseViewportWidth( JScrollPane scrollPane ) {
return scrollPane.getComponentOrientation().isLeftToRight()
? !isCornerVisible( upperRight ) && !isCornerVisible( lowerRight )
: !isCornerVisible( upperLeft ) && !isCornerVisible( lowerLeft );
}
private static boolean isCornerVisible( Component corner ) {
return corner != null &&
corner.getWidth() > 0 &&
corner.getHeight() > 0 &&
corner.isVisible();
}
private static void resizeViewport( Component c, int extraWidth, boolean ltr ) {
if( c == null )
return;
Rectangle vr = c.getBounds();
c.setBounds( vr.x - (ltr ? 0 : extraWidth), vr.y, vr.width + extraWidth, vr.height );
} }
} }
} }

View File

@@ -16,7 +16,9 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Canvas;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Graphics; import java.awt.Graphics;
@@ -67,6 +69,8 @@ import com.formdev.flatlaf.util.UIScale;
* <!-- FlatSplitPaneUI --> * <!-- FlatSplitPaneUI -->
* *
* @uiDefault Component.arrowType String chevron (default) or triangle * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault SplitPaneDivider.hoverColor Color optional
* @uiDefault SplitPaneDivider.pressedColor Color optional
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color * @uiDefault SplitPaneDivider.oneTouchArrowColor Color
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color * @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
* @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color * @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color
@@ -83,6 +87,7 @@ public class FlatSplitPaneUI
implements StyleableUI implements StyleableUI
{ {
@Styleable protected String arrowType; @Styleable protected String arrowType;
/** @since 3.3 */ @Styleable protected Color draggingColor;
@Styleable protected Color oneTouchArrowColor; @Styleable protected Color oneTouchArrowColor;
@Styleable protected Color oneTouchHoverArrowColor; @Styleable protected Color oneTouchHoverArrowColor;
@Styleable protected Color oneTouchPressedArrowColor; @Styleable protected Color oneTouchPressedArrowColor;
@@ -104,6 +109,8 @@ public class FlatSplitPaneUI
protected void installDefaults() { protected void installDefaults() {
arrowType = UIManager.getString( "Component.arrowType" ); arrowType = UIManager.getString( "Component.arrowType" );
draggingColor = UIManager.getColor( "SplitPaneDivider.draggingColor" );
// get one-touch colors before invoking super.installDefaults() because they are // get one-touch colors before invoking super.installDefaults() because they are
// used in there on LaF switching // used in there on LaF switching
oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" ); oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" );
@@ -117,6 +124,8 @@ public class FlatSplitPaneUI
protected void uninstallDefaults() { protected void uninstallDefaults() {
super.uninstallDefaults(); super.uninstallDefaults();
draggingColor = null;
oneTouchArrowColor = null; oneTouchArrowColor = null;
oneTouchHoverArrowColor = null; oneTouchHoverArrowColor = null;
oneTouchPressedArrowColor = null; oneTouchPressedArrowColor = null;
@@ -183,12 +192,49 @@ public class FlatSplitPaneUI
return FlatStylingSupport.getAnnotatedStyleableValue( this, key ); return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
} }
@Override
protected Component createDefaultNonContinuousLayoutDivider() {
// only used for non-continuous layout if left or right component is heavy weight
return new Canvas() {
@Override
public void paint( Graphics g ) {
if( !isContinuousLayout() && getLastDragLocation() != -1 )
paintDragDivider( g, 0 );
}
};
}
@Override
public void finishedPaintingChildren( JSplitPane sp, Graphics g ) {
if( sp == splitPane && getLastDragLocation() != -1 && !isContinuousLayout() && !draggingHW )
paintDragDivider( g, getLastDragLocation() );
}
private void paintDragDivider( Graphics g, int dividerLocation ) {
// divider bounds
boolean horizontal = (getOrientation() == JSplitPane.HORIZONTAL_SPLIT);
int x = horizontal ? dividerLocation : 0;
int y = !horizontal ? dividerLocation : 0;
int width = horizontal ? dividerSize : splitPane.getWidth();
int height = !horizontal ? dividerSize : splitPane.getHeight();
// paint background
g.setColor( FlatUIUtils.deriveColor( draggingColor, splitPane.getBackground() ) );
g.fillRect( x, y, width, height );
// paint divider style (e.g. grip)
if( divider instanceof FlatSplitPaneDivider )
((FlatSplitPaneDivider)divider).paintStyle( g, x, y, width, height );
}
//---- class FlatSplitPaneDivider ----------------------------------------- //---- class FlatSplitPaneDivider -----------------------------------------
protected class FlatSplitPaneDivider protected class FlatSplitPaneDivider
extends BasicSplitPaneDivider extends BasicSplitPaneDivider
{ {
@Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" ); @Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" );
/** @since 3.3 */ @Styleable protected Color hoverColor = UIManager.getColor( "SplitPaneDivider.hoverColor" );
/** @since 3.3 */ @Styleable protected Color pressedColor = UIManager.getColor( "SplitPaneDivider.pressedColor" );
@Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" ); @Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
@Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 ); @Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
@Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 ); @Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
@@ -251,15 +297,31 @@ public class FlatSplitPaneUI
@Override @Override
public void paint( Graphics g ) { public void paint( Graphics g ) {
// paint hover or pressed background
Color hoverOrPressedColor = (isContinuousLayout() && dragger != null)
? pressedColor
: (isMouseOver() && dragger == null
? hoverColor
: null);
if( hoverOrPressedColor != null ) {
g.setColor( FlatUIUtils.deriveColor( hoverOrPressedColor, splitPane.getBackground() ) );
g.fillRect( 0, 0, getWidth(), getHeight() );
}
super.paint( g ); super.paint( g );
paintStyle( g, 0, 0, getWidth(), getHeight() );
}
/** @since 3.3 */
protected void paintStyle( Graphics g, int x, int y, int width, int height ) {
if( "plain".equals( style ) ) if( "plain".equals( style ) )
return; return;
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g ); Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
g.setColor( gripColor ); g.setColor( gripColor );
paintGrip( g, 0, 0, getWidth(), getHeight() ); paintGrip( g, x, y, width, height );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints ); FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
} }
@@ -286,6 +348,29 @@ public class FlatSplitPaneUI
: location == (splitPane.getWidth() - getWidth() - insets.right); : location == (splitPane.getWidth() - getWidth() - insets.right);
} }
@Override
protected void setMouseOver( boolean mouseOver ) {
super.setMouseOver( mouseOver );
repaintIfNecessary();
}
@Override
protected void prepareForDragging() {
super.prepareForDragging();
repaintIfNecessary();
}
@Override
protected void finishDraggingTo( int location ) {
super.finishDraggingTo( location );
repaintIfNecessary();
}
private void repaintIfNecessary() {
if( hoverColor != null || pressedColor != null )
repaint();
}
//---- class FlatOneTouchButton --------------------------------------- //---- class FlatOneTouchButton ---------------------------------------
protected class FlatOneTouchButton protected class FlatOneTouchButton

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
@@ -28,16 +29,15 @@ import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import javax.swing.CellRendererPane;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.MouseInputListener; import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableHeaderUI; import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.JTableHeader; import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer; import javax.swing.table.TableCellRenderer;
@@ -114,6 +114,11 @@ public class FlatTableHeaderUI
public void installUI( JComponent c ) { public void installUI( JComponent c ) {
super.installUI( c ); super.installUI( c );
// replace cell renderer pane
header.remove( rendererPane );
rendererPane = new FlatTableHeaderCellRendererPane();
header.add( rendererPane );
installStyle(); installStyle();
} }
@@ -265,16 +270,8 @@ public class FlatTableHeaderUI
} }
} }
// temporary use own default renderer
FlatTableCellHeaderRenderer tempRenderer = new FlatTableCellHeaderRenderer( header.getDefaultRenderer() );
header.setDefaultRenderer( tempRenderer );
// paint header // paint header
super.paint( g, c ); super.paint( g, c );
// restore default renderer
tempRenderer.reset();
header.setDefaultRenderer( tempRenderer.delegate );
} }
private boolean isSystemDefaultRenderer( Object headerRenderer ) { private boolean isSystemDefaultRenderer( Object headerRenderer ) {
@@ -332,119 +329,129 @@ public class FlatTableHeaderUI
return false; return false;
} }
//---- class FlatTableCellHeaderRenderer ---------------------------------- //---- class FlatTableHeaderCellRendererPane ------------------------------
/** /**
* A delegating header renderer that is only used to paint hover and pressed * Cell renderer pane that is used to paint hover and pressed background/foreground
* background/foreground and to paint sort arrows at top, bottom or left position. * and to paint sort arrows at top, bottom or left position.
*/ */
private class FlatTableCellHeaderRenderer private class FlatTableHeaderCellRendererPane
implements TableCellRenderer, Border, UIResource extends CellRendererPane
{ {
private final TableCellRenderer delegate; private final Icon ascendingSortIcon;
private final Icon descendingSortIcon;
private JLabel l; public FlatTableHeaderCellRendererPane() {
private Color oldBackground; ascendingSortIcon = UIManager.getIcon( "Table.ascendingSortIcon" );
private Color oldForeground; descendingSortIcon = UIManager.getIcon( "Table.descendingSortIcon" );
private Boolean oldOpaque;
private int oldHorizontalTextPosition = -1;
private Border origBorder;
private Icon sortIcon;
FlatTableCellHeaderRenderer( TableCellRenderer delegate ) {
this.delegate = delegate;
} }
@Override @Override
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, public void paintComponent( Graphics g, Component c, Container p, int x, int y, int w, int h, boolean shouldValidate ) {
boolean hasFocus, int row, int column ) if( !(c instanceof JLabel) ) {
{ super.paintComponent( g, c, p, x, y, w, h, shouldValidate );
Component c = delegate.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column ); return;
if( !(c instanceof JLabel) ) }
return c;
l = (JLabel) c; JLabel l = (JLabel) c;
Color oldBackground = null;
Color oldForeground = null;
boolean oldOpaque = false;
Icon oldIcon = null;
int oldHorizontalTextPosition = -1;
// hover and pressed background/foreground // hover and pressed background/foreground
TableColumn draggedColumn = header.getDraggedColumn(); TableColumn draggedColumn = header.getDraggedColumn();
Color background = null; Color background = null;
Color foreground = null; Color foreground = null;
if( draggedColumn != null && header.getTable().convertColumnIndexToView( draggedColumn.getModelIndex() ) == column ) { if( draggedColumn != null &&
header.getTable().convertColumnIndexToView( draggedColumn.getModelIndex() )
== getColumn( x - header.getDraggedDistance(), w ) )
{
background = pressedBackground; background = pressedBackground;
foreground = pressedForeground; foreground = pressedForeground;
} else if( getRolloverColumn() == column ) { } else if( getRolloverColumn() >= 0 && getRolloverColumn() == getColumn( x, w ) ) {
background = hoverBackground; background = hoverBackground;
foreground = hoverForeground; foreground = hoverForeground;
} }
if( background != null ) { if( background != null ) {
if( oldBackground == null ) oldBackground = l.getBackground();
oldBackground = l.getBackground(); oldOpaque = l.isOpaque();
if( oldOpaque == null )
oldOpaque = l.isOpaque();
l.setBackground( FlatUIUtils.deriveColor( background, header.getBackground() ) ); l.setBackground( FlatUIUtils.deriveColor( background, header.getBackground() ) );
l.setOpaque( true ); l.setOpaque( true );
} }
if( foreground != null ) { if( foreground != null ) {
if( oldForeground == null ) oldForeground = l.getForeground();
oldForeground = l.getForeground();
l.setForeground( FlatUIUtils.deriveColor( foreground, header.getForeground() ) ); l.setForeground( FlatUIUtils.deriveColor( foreground, header.getForeground() ) );
} }
// sort icon // sort icon position
if( sortIconPosition == SwingConstants.LEFT ) { Icon icon = l.getIcon();
// left boolean isSortIcon = (icon != null && (icon == ascendingSortIcon || icon == descendingSortIcon));
if( oldHorizontalTextPosition < 0 ) if( isSortIcon ) {
if( sortIconPosition == SwingConstants.LEFT ) {
// left
oldHorizontalTextPosition = l.getHorizontalTextPosition(); oldHorizontalTextPosition = l.getHorizontalTextPosition();
l.setHorizontalTextPosition( SwingConstants.RIGHT ); l.setHorizontalTextPosition( SwingConstants.RIGHT );
} else if( sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM ) { } else if( sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM ) {
// top or bottom // top or bottom
sortIcon = l.getIcon(); oldIcon = icon;
origBorder = l.getBorder(); l.setIcon( null );
l.setIcon( null ); }
l.setBorder( this );
} }
return l; // paint renderer component
} super.paintComponent( g, c, p, x, y, w, h, shouldValidate );
void reset() { // paint top or bottom sort icon
if( l == null ) if( isSortIcon && (sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM) ) {
return; int xi = x + ((w - icon.getIconWidth()) / 2);
int yi = (sortIconPosition == SwingConstants.TOP)
? y + UIScale.scale( 1 )
: y + height - icon.getIconHeight()
- 1 // for gap
- (int) (1 * UIScale.getUserScaleFactor()); // for bottom border
icon.paintIcon( c, g, xi, yi );
}
if( oldBackground != null ) // restore modified renderer component properties
if( background != null ) {
l.setBackground( oldBackground ); l.setBackground( oldBackground );
if( oldForeground != null )
l.setForeground( oldForeground );
if( oldOpaque != null )
l.setOpaque( oldOpaque ); l.setOpaque( oldOpaque );
}
if( foreground != null )
l.setForeground( oldForeground );
if( oldIcon != null )
l.setIcon( oldIcon );
if( oldHorizontalTextPosition >= 0 ) if( oldHorizontalTextPosition >= 0 )
l.setHorizontalTextPosition( oldHorizontalTextPosition ); l.setHorizontalTextPosition( oldHorizontalTextPosition );
} }
@Override /**
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { * Get column index for given coordinates.
if( origBorder != null ) */
origBorder.paintBorder( c, g, x, y, width, height ); private int getColumn( int x, int width ) {
TableColumnModel columnModel = header.getColumnModel();
int columnCount = columnModel.getColumnCount();
boolean ltr = header.getComponentOrientation().isLeftToRight();
int cx = ltr ? 0 : getWidthInRightToLef();
if( sortIcon != null ) { for( int i = 0; i < columnCount; i++ ) {
int xi = x + ((width - sortIcon.getIconWidth()) / 2); int cw = columnModel.getColumn( i ).getWidth();
int yi = (sortIconPosition == SwingConstants.TOP) if( x == cx - (ltr ? 0 : cw) && width == cw )
? y + UIScale.scale( 1 ) return i;
: y + height - sortIcon.getIconHeight()
- 1 // for gap cx += ltr ? cw : -cw;
- (int) (1 * UIScale.getUserScaleFactor()); // for bottom border
sortIcon.paintIcon( c, g, xi, yi );
} }
return -1;
} }
@Override // similar to JTableHeader.getWidthInRightToLeft()
public Insets getBorderInsets( Component c ) { private int getWidthInRightToLef() {
return (origBorder != null) ? origBorder.getBorderInsets( c ) : new Insets( 0, 0, 0, 0 ); JTable table = header.getTable();
} return (table != null && table.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF)
? table.getWidth()
@Override : header.getWidth();
public boolean isBorderOpaque() {
return (origBorder != null) ? origBorder.isBorderOpaque() : false;
} }
} }

View File

@@ -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.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.EventQueue; import java.awt.EventQueue;
@@ -26,18 +27,25 @@ import java.awt.Insets;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport; import javax.swing.JViewport;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableUI; import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader; import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Graphics2DProxy; import com.formdev.flatlaf.util.Graphics2DProxy;
@@ -116,6 +124,7 @@ public class FlatTableUI
private boolean oldShowHorizontalLines; private boolean oldShowHorizontalLines;
private boolean oldShowVerticalLines; private boolean oldShowVerticalLines;
private Dimension oldIntercellSpacing; private Dimension oldIntercellSpacing;
private TableCellRenderer oldBooleanRenderer;
private PropertyChangeListener propertyChangeListener; private PropertyChangeListener propertyChangeListener;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
@@ -151,19 +160,35 @@ public class FlatTableUI
if( rowHeight > 0 ) if( rowHeight > 0 )
LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) ); LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) );
if( !showHorizontalLines ) { FlatTablePropertyWatcher watcher = FlatTablePropertyWatcher.get( table );
if( watcher != null )
watcher.enabled = false;
if( !showHorizontalLines && (watcher == null || !watcher.showHorizontalLinesChanged) ) {
oldShowHorizontalLines = table.getShowHorizontalLines(); oldShowHorizontalLines = table.getShowHorizontalLines();
table.setShowHorizontalLines( false ); table.setShowHorizontalLines( false );
} }
if( !showVerticalLines ) { if( !showVerticalLines && (watcher == null || !watcher.showVerticalLinesChanged) ) {
oldShowVerticalLines = table.getShowVerticalLines(); oldShowVerticalLines = table.getShowVerticalLines();
table.setShowVerticalLines( false ); table.setShowVerticalLines( false );
} }
if( intercellSpacing != null ) { if( intercellSpacing != null && (watcher == null || !watcher.intercellSpacingChanged) ) {
oldIntercellSpacing = table.getIntercellSpacing(); oldIntercellSpacing = table.getIntercellSpacing();
table.setIntercellSpacing( intercellSpacing ); table.setIntercellSpacing( intercellSpacing );
} }
if( watcher != null )
watcher.enabled = true;
else
table.addPropertyChangeListener( new FlatTablePropertyWatcher() );
// install boolean renderer
oldBooleanRenderer = table.getDefaultRenderer( Boolean.class );
if( oldBooleanRenderer instanceof UIResource )
table.setDefaultRenderer( Boolean.class, new FlatBooleanRenderer() );
else
oldBooleanRenderer = null;
} }
@Override @Override
@@ -177,15 +202,36 @@ public class FlatTableUI
oldStyleValues = null; oldStyleValues = null;
FlatTablePropertyWatcher watcher = FlatTablePropertyWatcher.get( table );
if( watcher != null )
watcher.enabled = false;
// restore old show horizontal/vertical lines (if not modified) // restore old show horizontal/vertical lines (if not modified)
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() ) if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() &&
table.setShowHorizontalLines( true ); (watcher == null || !watcher.showHorizontalLinesChanged) )
if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() ) table.setShowHorizontalLines( true );
table.setShowVerticalLines( true ); if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() &&
(watcher == null || !watcher.showVerticalLinesChanged) )
table.setShowVerticalLines( true );
// restore old intercell spacing (if not modified) // restore old intercell spacing (if not modified)
if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) ) if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) &&
table.setIntercellSpacing( oldIntercellSpacing ); (watcher == null || !watcher.intercellSpacingChanged) )
table.setIntercellSpacing( oldIntercellSpacing );
if( watcher != null )
watcher.enabled = true;
// uninstall boolean renderer
if( table.getDefaultRenderer( Boolean.class ) instanceof FlatBooleanRenderer ) {
if( oldBooleanRenderer instanceof Component ) {
// because the old renderer component was not attached to any component hierarchy,
// its UI was not yet updated, and it is necessary to do it here
SwingUtilities.updateComponentTreeUI( (Component) oldBooleanRenderer );
}
table.setDefaultRenderer( Boolean.class, oldBooleanRenderer );
}
oldBooleanRenderer = null;
} }
@Override @Override
@@ -467,4 +513,70 @@ public class FlatTableUI
} }
} }
} }
//---- class FlatTablePropertyWatcher -------------------------------------
/**
* Listener that watches for change of some table properties from application code.
* This information is used in {@link FlatTableUI#installDefaults()} and
* {@link FlatTableUI#uninstallDefaults()} to decide whether FlatLaf modifies those properties.
* If they are modified in application code, FlatLaf no longer changes them.
*
* The listener is added once for each table, but never removed.
* So switching Laf/theme reuses existing listener.
*/
private static class FlatTablePropertyWatcher
implements PropertyChangeListener
{
boolean enabled = true;
boolean showHorizontalLinesChanged;
boolean showVerticalLinesChanged;
boolean intercellSpacingChanged;
static FlatTablePropertyWatcher get( JTable table ) {
for( PropertyChangeListener l : table.getPropertyChangeListeners() ) {
if( l instanceof FlatTablePropertyWatcher )
return (FlatTablePropertyWatcher) l;
}
return null;
}
//---- interface PropertyChangeListener ----
@Override
public void propertyChange( PropertyChangeEvent e ) {
if( !enabled )
return;
switch( e.getPropertyName() ) {
case "showHorizontalLines": showHorizontalLinesChanged = true; break;
case "showVerticalLines": showVerticalLinesChanged = true; break;
case "rowMargin": intercellSpacingChanged = true; break;
}
}
}
//---- class FlatBooleanRenderer ------------------------------------------
private static class FlatBooleanRenderer
extends DefaultTableCellRenderer
implements UIResource
{
private boolean selected;
FlatBooleanRenderer() {
setHorizontalAlignment( SwingConstants.CENTER );
setIcon( new FlatCheckBoxIcon() {
@Override
protected boolean isSelected( Component c ) {
return selected;
}
} );
}
@Override
protected void setValue( Object value ) {
selected = (value != null && (Boolean) value);
}
}
} }

View File

@@ -608,6 +608,10 @@ public class FlatTitlePane
doLayout(); doLayout();
} }
void menuBarInvalidate() {
menuBarPlaceholder.invalidate();
}
@Override @Override
public void paint( Graphics g ) { public void paint( Graphics g ) {
super.paint( g ); super.paint( g );
@@ -835,10 +839,6 @@ public class FlatTitlePane
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) ); window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) );
} }
private boolean hasJBRCustomDecoration() {
return window != null && JBRCustomDecorations.hasCustomDecoration( window );
}
/** /**
* Returns whether windows uses native window border and has custom decorations enabled. * Returns whether windows uses native window border and has custom decorations enabled.
*/ */
@@ -896,10 +896,7 @@ public class FlatTitlePane
iconBounds.width += iconInsets.right; iconBounds.width += iconInsets.right;
} }
if( hasJBRCustomDecoration() ) appIconBounds = iconBounds;
hitTestSpots.add( iconBounds );
else
appIconBounds = iconBounds;
} else if( showIconBesideTitle && titleLabel.getIcon() != null && titleLabel.getUI() instanceof FlatTitleLabelUI ) { } else if( showIconBesideTitle && titleLabel.getIcon() != null && titleLabel.getUI() instanceof FlatTitleLabelUI ) {
FlatTitleLabelUI ui = (FlatTitleLabelUI) titleLabel.getUI(); FlatTitleLabelUI ui = (FlatTitleLabelUI) titleLabel.getUI();
@@ -927,10 +924,7 @@ public class FlatTitlePane
iconR.width += 2; iconR.width += 2;
iconR.height += 2; iconR.height += 2;
if( hasJBRCustomDecoration() ) appIconBounds = iconR;
hitTestSpots.add( iconR );
else
appIconBounds = iconR;
} }
} }
@@ -1270,7 +1264,7 @@ debug*/
public void mouseClicked( MouseEvent e ) { public void mouseClicked( MouseEvent e ) {
// on Linux, when using native library, the mouse clicked event // on Linux, when using native library, the mouse clicked event
// is usually not sent and maximize/restore is done in mouse pressed event // is usually not sent and maximize/restore is done in mouse pressed event
// this check is here for the case that a mouse clicked event comes thru for some reason // this check is here for the case that a mouse clicked event comes through for some reason
if( linuxNativeMove && SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) { if( linuxNativeMove && SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) {
// see comment in mousePressed() // see comment in mousePressed()
if( lastSingleClickWhen != 0 && (e.getWhen() - lastSingleClickWhen) <= getMultiClickInterval() ) { if( lastSingleClickWhen != 0 && (e.getWhen() - lastSingleClickWhen) <= getMultiClickInterval() ) {

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color; import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
@@ -173,6 +174,12 @@ public class FlatToolBarSeparatorUI
if( size != null ) if( size != null )
return scale( size ); return scale( size );
// get separator width
int separatorWidth = this.separatorWidth;
FlatToolBarUI toolBarUI = getToolBarUI( c );
if( toolBarUI != null && toolBarUI.separatorWidth != null )
separatorWidth = toolBarUI.separatorWidth;
// make sure that gap on left and right side of line have same size // make sure that gap on left and right side of line have same size
int sepWidth = (scale( (separatorWidth - LINE_WIDTH) / 2 ) * 2) + scale( LINE_WIDTH ); int sepWidth = (scale( (separatorWidth - LINE_WIDTH) / 2 ) * 2) + scale( LINE_WIDTH );
@@ -196,6 +203,12 @@ public class FlatToolBarSeparatorUI
float lineWidth = scale( 1f ); float lineWidth = scale( 1f );
float offset = scale( 2f ); float offset = scale( 2f );
// get separator color
Color separatorColor = this.separatorColor;
FlatToolBarUI toolBarUI = getToolBarUI( c );
if( toolBarUI != null && toolBarUI.separatorColor != null )
separatorColor = toolBarUI.separatorColor;
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g ); Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
g.setColor( separatorColor ); g.setColor( separatorColor );
@@ -210,4 +223,11 @@ public class FlatToolBarSeparatorUI
private boolean isVertical( JComponent c ) { private boolean isVertical( JComponent c ) {
return ((JToolBar.Separator)c).getOrientation() == SwingConstants.VERTICAL; return ((JToolBar.Separator)c).getOrientation() == SwingConstants.VERTICAL;
} }
private FlatToolBarUI getToolBarUI( JComponent c ) {
Container parent = c.getParent();
return (parent instanceof JToolBar && ((JToolBar)parent).getUI() instanceof FlatToolBarUI)
? (FlatToolBarUI) ((JToolBar)parent).getUI()
: null;
}
} }

View File

@@ -93,6 +93,10 @@ public class FlatToolBarUI
@Styleable protected Insets borderMargins; @Styleable protected Insets borderMargins;
@Styleable protected Color gripColor; @Styleable protected Color gripColor;
// for FlatToolBarSeparatorUI
/** @since 3.3 */ @Styleable protected Integer separatorWidth;
/** @since 3.3 */ @Styleable protected Color separatorColor;
private FocusTraversalPolicy focusTraversalPolicy; private FocusTraversalPolicy focusTraversalPolicy;
private Boolean oldFloatable; private Boolean oldFloatable;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;

View File

@@ -299,15 +299,10 @@ public class FlatUIUtils
if( c == null ) if( c == null )
return false; return false;
// check whether used in cell editor (check 3 levels up) // check whether used as table cell editor
Component c2 = c; Container parent = c.getParent();
for( int i = 0; i <= 2 && c2 != null; i++ ) { if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c )
Container parent = c2.getParent(); return true;
if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c2 )
return true;
c2 = parent;
}
// 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
@@ -734,7 +729,7 @@ public class FlatUIUtils
} }
/** /**
* Creates a (rounded) rectangle used to paint components (border, background, etc). * Creates a (rounded) rectangle used to paint components (border, background, etc.).
* The given arc diameter is limited to min(width,height). * The given arc diameter is limited to min(width,height).
*/ */
public static Shape createComponentRectangle( float x, float y, float w, float h, float arc ) { public static Shape createComponentRectangle( float x, float y, float w, float h, float arc ) {

View File

@@ -18,7 +18,6 @@ package com.formdev.flatlaf.ui;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics; import java.awt.Graphics;
import java.lang.reflect.Method;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JViewport; import javax.swing.JViewport;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
@@ -48,14 +47,9 @@ public class FlatViewportUI
Component view = ((JViewport)c).getView(); Component view = ((JViewport)c).getView();
if( view instanceof JComponent ) { if( view instanceof JComponent ) {
try { ComponentUI ui = JavaCompatibility2.getUI( (JComponent) view );
Method m = view.getClass().getMethod( "getUI" ); if( ui instanceof ViewportPainter )
Object ui = m.invoke( view ); ((ViewportPainter)ui).paintViewport( g, (JComponent) view, (JViewport) c );
if( ui instanceof ViewportPainter )
((ViewportPainter)ui).paintViewport( g, (JComponent) view, (JViewport) c );
} catch( Exception ex ) {
// ignore
}
} }
} }

View File

@@ -1,306 +0,0 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.BorderUIResource;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Support for custom window decorations provided by JetBrains Runtime (based on OpenJDK).
* Requires that the application runs on Windows 10 in a JetBrains Runtime 11 or later.
* <ul>
* <li><a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime</a></li>
* <li><a href="https://github.com/JetBrains/JetBrainsRuntime">https://github.com/JetBrains/JetBrainsRuntime</a></li>
* </ul>
*
* @author Karl Tauber
*/
public class JBRCustomDecorations
{
private static Boolean supported;
private static Method Window_hasCustomDecoration;
private static Method Window_setHasCustomDecoration;
private static Method WWindowPeer_setCustomDecorationTitleBarHeight;
private static Method WWindowPeer_setCustomDecorationHitTestSpots;
private static Method AWTAccessor_getComponentAccessor;
private static Method AWTAccessor_ComponentAccessor_getPeer;
public static boolean isSupported() {
initialize();
return supported;
}
static Object install( JRootPane rootPane ) {
if( !isSupported() )
return null;
// check whether root pane already has a parent, which is the case when switching LaF
Container parent = rootPane.getParent();
if( parent != null ) {
if( parent instanceof Window )
FlatNativeWindowBorder.install( (Window) parent );
return null;
}
// Use hierarchy listener to wait until the root pane is added to a window.
// Enabling JBR decorations must be done very early, probably before
// window becomes displayable (window.isDisplayable()). Tried also using
// "ancestor" property change event on root pane, but this is invoked too late.
HierarchyListener addListener = new HierarchyListener() {
@Override
public void hierarchyChanged( HierarchyEvent e ) {
if( e.getChanged() != rootPane || (e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) == 0 )
return;
Container parent = e.getChangedParent();
if( parent instanceof Window )
FlatNativeWindowBorder.install( (Window) parent );
// remove listener since it is actually not possible to uninstall JBR decorations
// use invokeLater to remove listener to avoid that listener
// is removed while listener queue is processed
EventQueue.invokeLater( () -> {
rootPane.removeHierarchyListener( this );
} );
}
};
rootPane.addHierarchyListener( addListener );
return addListener;
}
static void uninstall( JRootPane rootPane, Object data ) {
// remove listener (if not yet done)
if( data instanceof HierarchyListener )
rootPane.removeHierarchyListener( (HierarchyListener) data );
// since it is actually not possible to uninstall JBR decorations,
// simply reduce titleBarHeight so that it is still possible to resize window
// and remove hitTestSpots
Container parent = rootPane.getParent();
if( parent instanceof Window )
setHasCustomDecoration( (Window) parent, false );
}
static boolean hasCustomDecoration( Window window ) {
if( !isSupported() )
return false;
try {
return (Boolean) Window_hasCustomDecoration.invoke( window );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
return false;
}
}
static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
if( !isSupported() )
return;
try {
if( hasCustomDecoration )
Window_setHasCustomDecoration.invoke( window );
else
setTitleBarHeightAndHitTestSpots( window, 4, Collections.emptyList() );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, List<Rectangle> hitTestSpots ) {
if( !isSupported() )
return;
try {
Object compAccessor = AWTAccessor_getComponentAccessor.invoke( null );
Object peer = AWTAccessor_ComponentAccessor_getPeer.invoke( compAccessor, window );
WWindowPeer_setCustomDecorationTitleBarHeight.invoke( peer, titleBarHeight );
WWindowPeer_setCustomDecorationHitTestSpots.invoke( peer, hitTestSpots );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
private static void initialize() {
if( supported != null )
return;
supported = false;
// requires JetBrains Runtime 11 and Windows 10
if( !SystemInfo.isJetBrainsJVM_11_orLater || !SystemInfo.isWindows_10_orLater )
return;
try {
Class<?> awtAcessorClass = Class.forName( "sun.awt.AWTAccessor" );
Class<?> compAccessorClass = Class.forName( "sun.awt.AWTAccessor$ComponentAccessor" );
AWTAccessor_getComponentAccessor = awtAcessorClass.getDeclaredMethod( "getComponentAccessor" );
AWTAccessor_ComponentAccessor_getPeer = compAccessorClass.getDeclaredMethod( "getPeer", Component.class );
Class<?> peerClass = Class.forName( "sun.awt.windows.WWindowPeer" );
WWindowPeer_setCustomDecorationTitleBarHeight = peerClass.getDeclaredMethod( "setCustomDecorationTitleBarHeight", int.class );
WWindowPeer_setCustomDecorationHitTestSpots = peerClass.getDeclaredMethod( "setCustomDecorationHitTestSpots", List.class );
WWindowPeer_setCustomDecorationTitleBarHeight.setAccessible( true );
WWindowPeer_setCustomDecorationHitTestSpots.setAccessible( true );
Window_hasCustomDecoration = Window.class.getDeclaredMethod( "hasCustomDecoration" );
Window_setHasCustomDecoration = Window.class.getDeclaredMethod( "setHasCustomDecoration" );
Window_hasCustomDecoration.setAccessible( true );
Window_setHasCustomDecoration.setAccessible( true );
supported = true;
} catch( Exception ex ) {
// ignore
}
}
//---- class JBRWindowTopBorder -------------------------------------------
static class JBRWindowTopBorder
extends BorderUIResource.EmptyBorderUIResource
{
private static JBRWindowTopBorder instance;
private final Color activeLightColor = new Color( 0x707070 );
private final Color activeDarkColor = new Color( 0x2D2E2F );
private final Color inactiveLightColor = new Color( 0xaaaaaa );
private final Color inactiveDarkColor = new Color( 0x494A4B );
private boolean colorizationAffectsBorders;
private Color activeColor;
static JBRWindowTopBorder getInstance() {
if( instance == null )
instance = new JBRWindowTopBorder();
return instance;
}
JBRWindowTopBorder() {
super( 1, 0, 0, 0 );
update();
installListeners();
}
void update() {
colorizationAffectsBorders = isColorizationColorAffectsBorders();
activeColor = calculateActiveBorderColor();
}
void installListeners() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.addPropertyChangeListener( "win.dwm.colorizationColor.affects.borders", e -> {
colorizationAffectsBorders = isColorizationColorAffectsBorders();
activeColor = calculateActiveBorderColor();
} );
PropertyChangeListener l = e -> {
activeColor = calculateActiveBorderColor();
};
toolkit.addPropertyChangeListener( "win.dwm.colorizationColor", l );
toolkit.addPropertyChangeListener( "win.dwm.colorizationColorBalance", l );
toolkit.addPropertyChangeListener( "win.frame.activeBorderColor", l );
}
boolean isColorizationColorAffectsBorders() {
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColor.affects.borders" );
return (value instanceof Boolean) ? (Boolean) value : true;
}
Color getColorizationColor() {
return (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColor" );
}
int getColorizationColorBalance() {
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColorBalance" );
return (value instanceof Integer) ? (Integer) value : -1;
}
private Color calculateActiveBorderColor() {
if( !colorizationAffectsBorders )
return null;
Color colorizationColor = getColorizationColor();
if( colorizationColor != null ) {
int colorizationColorBalance = getColorizationColorBalance();
if( colorizationColorBalance < 0 || colorizationColorBalance > 100 )
colorizationColorBalance = 100;
if( colorizationColorBalance == 0 )
return new Color( 0xD9D9D9 );
if( colorizationColorBalance == 100 )
return colorizationColor;
float alpha = colorizationColorBalance / 100.0f;
float remainder = 1 - alpha;
int r = Math.round( colorizationColor.getRed() * alpha + 0xD9 * remainder );
int g = Math.round( colorizationColor.getGreen() * alpha + 0xD9 * remainder );
int b = Math.round( colorizationColor.getBlue() * alpha + 0xD9 * remainder );
// avoid potential IllegalArgumentException in Color constructor
r = Math.min( Math.max( r, 0 ), 255 );
g = Math.min( Math.max( g, 0 ), 255 );
b = Math.min( Math.max( b, 0 ), 255 );
return new Color( r, g, b );
}
Color activeBorderColor = (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.frame.activeBorderColor" );
return (activeBorderColor != null) ? activeBorderColor : UIManager.getColor( "MenuBar.borderColor" );
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Window window = SwingUtilities.windowForComponent( c );
boolean active = window != null && window.isActive();
boolean dark = FlatLaf.isLafDark();
g.setColor( active
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
: (dark ? inactiveDarkColor : inactiveLightColor) );
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
}
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
g.fillRect( x, y, width, 1 );
}
void repaintBorder( Component c ) {
c.repaint( 0, 0, c.getWidth(), 1 );
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.plaf.ComponentUI;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Provides Java version compatibility methods.
* <p>
* WARNING: This is private API and may change.
*
* @author Karl Tauber
* @since 3.3
*/
public class JavaCompatibility2
{
private static boolean getUIMethodInitialized;
private static MethodHandle getUIMethod;
/**
* Java 8: getUI() method on various components (e.g. JButton, JList, etc)
* <br>
* Java 9: javax.swing.JComponent.getUI()
*/
public static ComponentUI getUI( JComponent c ) {
try {
// Java 9+
if( SystemInfo.isJava_9_orLater ) {
if( !getUIMethodInitialized ) {
getUIMethodInitialized = true;
try {
MethodType mt = MethodType.methodType( ComponentUI.class, new Class[0] );
getUIMethod = MethodHandles.publicLookup().findVirtual( JComponent.class, "getUI", mt );
} catch( Exception ex ) {
// ignore
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
if( getUIMethod != null )
return (ComponentUI) getUIMethod.invoke( c );
}
// components often used (e.g. as view in scroll panes)
if( c instanceof JPanel )
return ((JPanel)c).getUI();
if( c instanceof JList )
return ((JList<?>)c).getUI();
if( c instanceof JTable )
return ((JTable)c).getUI();
if( c instanceof JTree )
return ((JTree)c).getUI();
if( c instanceof JTextComponent )
return ((JTextComponent)c).getUI();
// Java 8 and fallback
Method m = c.getClass().getMethod( "getUI" );
return (ComponentUI) m.invoke( c );
} catch( Throwable ex ) {
// ignore
return null;
}
}
}

View File

@@ -80,7 +80,7 @@ public class FontUtils
/** /**
* Loads a font family previously registered via {@link #registerFontFamilyLoader(String, Runnable)}. * Loads a font family previously registered via {@link #registerFontFamilyLoader(String, Runnable)}.
* If the family is already loaded or no londer is registered for that family, nothing happens. * If the family is already loaded or no loader is registered for that family, nothing happens.
*/ */
public static void loadFontFamily( String family ) { public static void loadFontFamily( String family ) {
if( !hasLoaders() ) if( !hasLoaders() )
@@ -109,7 +109,7 @@ public class FontUtils
} }
/** /**
* Returns all font familiy names available in the graphics environment. * Returns all font family names available in the graphics environment.
* This invokes {@link GraphicsEnvironment#getAvailableFontFamilyNames()} and * This invokes {@link GraphicsEnvironment#getAvailableFontFamilyNames()} and
* appends families registered for lazy loading via {@link #registerFontFamilyLoader(String, Runnable)} * appends families registered for lazy loading via {@link #registerFontFamilyLoader(String, Runnable)}
* to the result. * to the result.

View File

@@ -192,7 +192,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":

View File

@@ -116,7 +116,11 @@ public class NativeLibrary
try { try {
// for development environment // for development environment
if( "file".equals( libraryUrl.getProtocol() ) ) { if( "file".equals( libraryUrl.getProtocol() ) ) {
File libraryFile = new File( libraryUrl.getPath() ); String binPath = libraryUrl.getPath();
String srcPath = binPath.replace( "flatlaf-core/bin/main/", "flatlaf-core/src/main/resources/" );
File libraryFile = new File( srcPath ); // use from 'src' folder if available
if( !libraryFile.isFile() )
libraryFile = new File( binPath ); // use from 'bin' or 'output' folder if available
if( libraryFile.isFile() ) { if( libraryFile.isFile() ) {
// load library without copying // load library without copying
System.load( libraryFile.getCanonicalPath() ); System.load( libraryFile.getCanonicalPath() );

View File

@@ -204,7 +204,7 @@ public class UIScale
if( SystemInfo.isWindows ) { if( SystemInfo.isWindows ) {
// Special handling for Windows to be compatible with OS scaling, // Special handling for Windows to be compatible with OS scaling,
// which distinguish between "screen scaling" and "text scaling". // which distinguish between "screen scaling" and "text scaling".
// - Windows "screen scaling" scales everything (text, icon, gaps, etc) // - Windows "screen scaling" scales everything (text, icon, gaps, etc.)
// and may have different scaling factors for each screen. // and may have different scaling factors for each screen.
// - Windows "text scaling" increases only the font size, but on all screens. // - Windows "text scaling" increases only the font size, but on all screens.
// //

View File

@@ -246,6 +246,7 @@ PasswordField.revealIconColor = @foreground
#---- Popup ---- #---- Popup ----
[mac]Popup.roundedBorderWidth = 1
Popup.dropShadowColor = #000 Popup.dropShadowColor = #000
Popup.dropShadowOpacity = 0.25 Popup.dropShadowOpacity = 0.25

View File

@@ -289,6 +289,7 @@ ComboBox.popupInsets = 0,0,0,0
ComboBox.selectionInsets = 0,0,0,0 ComboBox.selectionInsets = 0,0,0,0
ComboBox.selectionArc = 0 ComboBox.selectionArc = 0
ComboBox.borderCornerRadius = $Popup.borderCornerRadius ComboBox.borderCornerRadius = $Popup.borderCornerRadius
[mac]ComboBox.roundedBorderWidth = $Popup.roundedBorderWidth
#---- Component ---- #---- Component ----
@@ -505,6 +506,7 @@ PasswordField.revealIcon = com.formdev.flatlaf.icons.FlatRevealIcon
#---- Popup ---- #---- Popup ----
Popup.borderCornerRadius = 4 Popup.borderCornerRadius = 4
[mac]Popup.roundedBorderWidth = 0
Popup.dropShadowPainted = true Popup.dropShadowPainted = true
Popup.dropShadowInsets = -4,-4,4,4 Popup.dropShadowInsets = -4,-4,4,4
@@ -514,6 +516,7 @@ Popup.dropShadowInsets = -4,-4,4,4
PopupMenu.border = com.formdev.flatlaf.ui.FlatPopupMenuBorder PopupMenu.border = com.formdev.flatlaf.ui.FlatPopupMenuBorder
PopupMenu.borderInsets = 4,1,4,1 PopupMenu.borderInsets = 4,1,4,1
PopupMenu.borderCornerRadius = $Popup.borderCornerRadius PopupMenu.borderCornerRadius = $Popup.borderCornerRadius
[mac]PopupMenu.roundedBorderWidth = $Popup.roundedBorderWidth
PopupMenu.background = @menuBackground PopupMenu.background = @menuBackground
PopupMenu.scrollArrowColor = @buttonArrowColor PopupMenu.scrollArrowColor = @buttonArrowColor
@@ -597,10 +600,15 @@ ScrollBar.allowsAbsolutePositioning = true
#---- ScrollPane ---- #---- ScrollPane ----
ScrollPane.border = com.formdev.flatlaf.ui.FlatBorder ScrollPane.border = com.formdev.flatlaf.ui.FlatScrollPaneBorder
ScrollPane.background = $ScrollBar.track ScrollPane.background = $ScrollBar.track
ScrollPane.fillUpperCorner = true ScrollPane.fillUpperCorner = true
ScrollPane.smoothScrolling = true ScrollPane.smoothScrolling = true
ScrollPane.arc = 0
#ScrollPane.List.arc = -1
#ScrollPane.Table.arc = -1
#ScrollPane.TextComponent.arc = -1
#ScrollPane.Tree.arc = -1
#---- SearchField ---- #---- SearchField ----
@@ -697,6 +705,8 @@ TabbedPane.tabAreaAlignment = leading
TabbedPane.tabAlignment = center TabbedPane.tabAlignment = center
# allowed values: preferred, equal or compact # allowed values: preferred, equal or compact
TabbedPane.tabWidthMode = preferred TabbedPane.tabWidthMode = preferred
# allowed values: none, auto, left or right
TabbedPane.tabRotation = none
# allowed values: underlined or card # allowed values: underlined or card
TabbedPane.tabType = underlined TabbedPane.tabType = underlined
@@ -731,7 +741,7 @@ Table.showVerticalLines = false
Table.showTrailingVerticalLine = false Table.showTrailingVerticalLine = false
Table.consistentHomeEndKeyBehavior = true Table.consistentHomeEndKeyBehavior = true
Table.intercellSpacing = 0,0 Table.intercellSpacing = 0,0
Table.scrollPaneBorder = com.formdev.flatlaf.ui.FlatBorder Table.scrollPaneBorder = com.formdev.flatlaf.ui.FlatScrollPaneBorder
Table.ascendingSortIcon = com.formdev.flatlaf.icons.FlatAscendingSortIcon Table.ascendingSortIcon = com.formdev.flatlaf.icons.FlatAscendingSortIcon
Table.descendingSortIcon = com.formdev.flatlaf.icons.FlatDescendingSortIcon Table.descendingSortIcon = com.formdev.flatlaf.icons.FlatDescendingSortIcon
Table.sortIconColor = @icon Table.sortIconColor = @icon
@@ -902,6 +912,7 @@ ToolTipManager.enableToolTipMode = activeApplication
#---- ToolTip ---- #---- ToolTip ----
ToolTip.borderCornerRadius = $Popup.borderCornerRadius ToolTip.borderCornerRadius = $Popup.borderCornerRadius
[mac]ToolTip.roundedBorderWidth = $Popup.roundedBorderWidth
#---- Tree ---- #---- Tree ----

View File

@@ -295,3 +295,8 @@ ToggleButton.disabledBackground = $Button.disabledBackground
ToggleButton.selectedBackground = lighten($ToggleButton.background,20%,derived) ToggleButton.selectedBackground = lighten($ToggleButton.background,20%,derived)
ToggleButton.toolbar.selectedBackground = #fff3 ToggleButton.toolbar.selectedBackground = #fff3
#---- ToolBar ----
ToolBar.hoverButtonGroupArc = 14

View File

@@ -291,3 +291,8 @@ TextPane.selectionForeground = @textSelectionForeground
#---- ToggleButton ---- #---- ToggleButton ----
ToggleButton.disabledBackground = $Button.disabledBackground ToggleButton.disabledBackground = $Button.disabledBackground
#---- ToolBar ----
ToolBar.hoverButtonGroupArc = 14

View File

@@ -601,7 +601,7 @@ public class TestFlatStyleableInfo
); );
// border // border
flatBorder( expected ); flatScrollPaneBorder( expected );
assertMapEquals( expected, ui.getStyleableInfos( c ) ); assertMapEquals( expected, ui.getStyleableInfos( c ) );
} }
@@ -689,6 +689,9 @@ public class TestFlatStyleableInfo
Map<String, Class<?>> expected = expectedMap( Map<String, Class<?>> expected = expectedMap(
"arrowType", String.class, "arrowType", String.class,
"draggingColor", Color.class,
"hoverColor", Color.class,
"pressedColor", Color.class,
"oneTouchArrowColor", Color.class, "oneTouchArrowColor", Color.class,
"oneTouchHoverArrowColor", Color.class, "oneTouchHoverArrowColor", Color.class,
"oneTouchPressedArrowColor", Color.class, "oneTouchPressedArrowColor", Color.class,
@@ -752,6 +755,7 @@ public class TestFlatStyleableInfo
"tabAreaAlignment", String.class, "tabAreaAlignment", String.class,
"tabAlignment", String.class, "tabAlignment", String.class,
"tabWidthMode", String.class, "tabWidthMode", String.class,
"tabRotation", String.class,
"arrowType", String.class, "arrowType", String.class,
"buttonInsets", Insets.class, "buttonInsets", Insets.class,
@@ -925,7 +929,10 @@ public class TestFlatStyleableInfo
"hoverButtonGroupBackground", Color.class, "hoverButtonGroupBackground", Color.class,
"borderMargins", Insets.class, "borderMargins", Insets.class,
"gripColor", Color.class "gripColor", Color.class,
"separatorWidth", Integer.class,
"separatorColor", Color.class
); );
assertMapEquals( expected, ui.getStyleableInfos( c ) ); assertMapEquals( expected, ui.getStyleableInfos( c ) );
@@ -1005,17 +1012,23 @@ public class TestFlatStyleableInfo
expectedMap( expected, expectedMap( expected,
"arc", int.class, "arc", int.class,
"roundRect", Boolean.class "roundRect", Boolean.class
); );
} }
private void flatScrollPaneBorder( Map<String, Class<?>> expected ) {
flatBorder( expected );
expectedMap( expected,
"arc", int.class
);
}
private void flatTextBorder( Map<String, Class<?>> expected ) { private void flatTextBorder( Map<String, Class<?>> expected ) {
flatBorder( expected ); flatBorder( expected );
expectedMap( expected, expectedMap( expected,
"arc", int.class, "arc", int.class,
"roundRect", Boolean.class "roundRect", Boolean.class
); );
} }

View File

@@ -625,7 +625,7 @@ public class TestFlatStyleableValue
FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI(); FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI();
// border // border
flatBorder( c, ui ); flatScrollPaneBorder( c, ui );
testBoolean( c, ui, "showButtons", true ); testBoolean( c, ui, "showButtons", true );
} }
@@ -699,6 +699,9 @@ public class TestFlatStyleableValue
FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI(); FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI();
testString( c, ui, "arrowType", "chevron" ); testString( c, ui, "arrowType", "chevron" );
testColor( c, ui, "draggingColor", 0x123456 );
testColor( c, ui, "hoverColor", 0x123456 );
testColor( c, ui, "pressedColor", 0x123456 );
testColor( c, ui, "oneTouchArrowColor", 0x123456 ); testColor( c, ui, "oneTouchArrowColor", 0x123456 );
testColor( c, ui, "oneTouchHoverArrowColor", 0x123456 ); testColor( c, ui, "oneTouchHoverArrowColor", 0x123456 );
testColor( c, ui, "oneTouchPressedArrowColor", 0x123456 ); testColor( c, ui, "oneTouchPressedArrowColor", 0x123456 );
@@ -758,6 +761,7 @@ public class TestFlatStyleableValue
testString( c, ui, "tabAreaAlignment", "leading" ); testString( c, ui, "tabAreaAlignment", "leading" );
testString( c, ui, "tabAlignment", "center" ); testString( c, ui, "tabAlignment", "center" );
testString( c, ui, "tabWidthMode", "preferred" ); testString( c, ui, "tabWidthMode", "preferred" );
testString( c, ui, "tabRotation", "none" );
testString( c, ui, "arrowType", "chevron" ); testString( c, ui, "arrowType", "chevron" );
testInsets( c, ui, "buttonInsets", 1,2,3,4 ); testInsets( c, ui, "buttonInsets", 1,2,3,4 );
@@ -902,6 +906,9 @@ public class TestFlatStyleableValue
testInsets( c, ui, "borderMargins", 1,2,3,4 ); testInsets( c, ui, "borderMargins", 1,2,3,4 );
testColor( c, ui, "gripColor", 0x123456 ); testColor( c, ui, "gripColor", 0x123456 );
testInteger( c, ui, "separatorWidth", 123 );
testColor( c, ui, "separatorColor", 0x123456 );
} }
@Test @Test
@@ -965,15 +972,19 @@ public class TestFlatStyleableValue
flatBorder( c, ui ); flatBorder( c, ui );
testInteger( c, ui, "arc", 123 ); testInteger( c, ui, "arc", 123 );
testBoolean( c, ui, "roundRect", true ); testBoolean( c, ui, "roundRect", true );
} }
private void flatScrollPaneBorder( JComponent c, StyleableUI ui ) {
flatBorder( c, ui );
testInteger( c, ui, "arc", 123 );
}
private void flatTextBorder( JComponent c, StyleableUI ui ) { private void flatTextBorder( JComponent c, StyleableUI ui ) {
flatBorder( c, ui ); flatBorder( c, ui );
testInteger( c, ui, "arc", 123 ); testInteger( c, ui, "arc", 123 );
testBoolean( c, ui, "roundRect", true ); testBoolean( c, ui, "roundRect", true );
} }
@@ -1034,6 +1045,17 @@ public class TestFlatStyleableValue
// FlatRoundBorder extends FlatBorder // FlatRoundBorder extends FlatBorder
flatBorder( border ); flatBorder( border );
testValue( border, "arc", 6 );
testValue( border, "roundRect", true );
}
@Test
void flatScrollPaneBorder() {
FlatScrollPaneBorder border = new FlatScrollPaneBorder();
// FlatScrollPaneBorder extends FlatBorder
flatBorder( border );
testValue( border, "arc", 6 ); testValue( border, "arc", 6 );
} }
@@ -1045,6 +1067,7 @@ public class TestFlatStyleableValue
flatBorder( border ); flatBorder( border );
testValue( border, "arc", 6 ); testValue( border, "arc", 6 );
testValue( border, "roundRect", true );
} }
@Test @Test

View File

@@ -760,7 +760,7 @@ public class TestFlatStyling
FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI(); FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI();
// border // border
flatBorder( style -> ui.applyStyle( style ) ); flatScrollPaneBorder( style -> ui.applyStyle( style ) );
ui.applyStyle( "showButtons: true" ); ui.applyStyle( "showButtons: true" );
@@ -870,6 +870,9 @@ public class TestFlatStyling
FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI(); FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI();
ui.applyStyle( "arrowType: chevron" ); ui.applyStyle( "arrowType: chevron" );
ui.applyStyle( "draggingColor: #fff" );
ui.applyStyle( "hoverColor: #fff" );
ui.applyStyle( "pressedColor: #fff" );
ui.applyStyle( "oneTouchArrowColor: #fff" ); ui.applyStyle( "oneTouchArrowColor: #fff" );
ui.applyStyle( "oneTouchHoverArrowColor: #fff" ); ui.applyStyle( "oneTouchHoverArrowColor: #fff" );
ui.applyStyle( "oneTouchPressedArrowColor: #fff" ); ui.applyStyle( "oneTouchPressedArrowColor: #fff" );
@@ -937,6 +940,7 @@ public class TestFlatStyling
ui.applyStyle( "tabAreaAlignment: leading" ); ui.applyStyle( "tabAreaAlignment: leading" );
ui.applyStyle( "tabAlignment: center" ); ui.applyStyle( "tabAlignment: center" );
ui.applyStyle( "tabWidthMode: preferred" ); ui.applyStyle( "tabWidthMode: preferred" );
ui.applyStyle( "tabRotation: none" );
ui.applyStyle( "arrowType: chevron" ); ui.applyStyle( "arrowType: chevron" );
ui.applyStyle( "buttonInsets: 1,2,3,4" ); ui.applyStyle( "buttonInsets: 1,2,3,4" );
@@ -1146,6 +1150,9 @@ public class TestFlatStyling
ui.applyStyle( "borderMargins: 1,2,3,4" ); ui.applyStyle( "borderMargins: 1,2,3,4" );
ui.applyStyle( "gripColor: #fff" ); ui.applyStyle( "gripColor: #fff" );
ui.applyStyle( "separatorWidth: 6" );
ui.applyStyle( "separatorColor: #fff" );
// JComponent properties // JComponent properties
ui.applyStyle( "background: #fff" ); ui.applyStyle( "background: #fff" );
ui.applyStyle( "foreground: #fff" ); ui.applyStyle( "foreground: #fff" );
@@ -1234,15 +1241,19 @@ public class TestFlatStyling
flatBorder( applyStyle ); flatBorder( applyStyle );
applyStyle.accept( "arc: 6" ); applyStyle.accept( "arc: 6" );
applyStyle.accept( "roundRect: true" ); applyStyle.accept( "roundRect: true" );
} }
private void flatScrollPaneBorder( Consumer<String> applyStyle ) {
flatBorder( applyStyle );
applyStyle.accept( "arc: 6" );
}
private void flatTextBorder( Consumer<String> applyStyle ) { private void flatTextBorder( Consumer<String> applyStyle ) {
flatBorder( applyStyle ); flatBorder( applyStyle );
applyStyle.accept( "arc: 6" ); applyStyle.accept( "arc: 6" );
applyStyle.accept( "roundRect: true" ); applyStyle.accept( "roundRect: true" );
} }

View File

@@ -28,6 +28,8 @@ import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.nimbus.NimbusLookAndFeel; import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import com.formdev.flatlaf.*; import com.formdev.flatlaf.*;
import com.formdev.flatlaf.extras.FlatAnimatedLafChange; import com.formdev.flatlaf.extras.FlatAnimatedLafChange;
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
import com.formdev.flatlaf.themes.FlatMacLightLaf;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
@@ -66,6 +68,8 @@ class ControlBar
lafModel.addElement( new LookAndFeelInfo( "FlatLaf Dark (F2)", FlatDarkLaf.class.getName() ) ); lafModel.addElement( new LookAndFeelInfo( "FlatLaf Dark (F2)", FlatDarkLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "FlatLaf IntelliJ (F3)", FlatIntelliJLaf.class.getName() ) ); lafModel.addElement( new LookAndFeelInfo( "FlatLaf IntelliJ (F3)", FlatIntelliJLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "FlatLaf Darcula (F4)", FlatDarculaLaf.class.getName() ) ); lafModel.addElement( new LookAndFeelInfo( "FlatLaf Darcula (F4)", FlatDarculaLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "FlatLaf macOS Light (F5)", FlatMacLightLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "FlatLaf macOS Dark (F6)", FlatMacDarkLaf.class.getName() ) );
UIManager.LookAndFeelInfo[] lookAndFeels = UIManager.getInstalledLookAndFeels(); UIManager.LookAndFeelInfo[] lookAndFeels = UIManager.getInstalledLookAndFeels();
for( UIManager.LookAndFeelInfo lookAndFeel : lookAndFeels ) { for( UIManager.LookAndFeelInfo lookAndFeel : lookAndFeels ) {
@@ -127,6 +131,8 @@ class ControlBar
registerSwitchToLookAndFeel( KeyEvent.VK_F2, FlatDarkLaf.class.getName() ); registerSwitchToLookAndFeel( KeyEvent.VK_F2, FlatDarkLaf.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F3, FlatIntelliJLaf.class.getName() ); registerSwitchToLookAndFeel( KeyEvent.VK_F3, FlatIntelliJLaf.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F4, FlatDarculaLaf.class.getName() ); registerSwitchToLookAndFeel( KeyEvent.VK_F4, FlatDarculaLaf.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F5, FlatMacLightLaf.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F6, FlatMacDarkLaf.class.getName() );
if( SystemInfo.isWindows ) if( SystemInfo.isWindows )
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" ); registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );

View File

@@ -18,7 +18,6 @@ package com.formdev.flatlaf.demo;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable; import java.awt.datatransfer.Transferable;
@@ -116,15 +115,16 @@ class DataComponentsPanel
table1.setGridColor( redGridColorCheckBox.isSelected() ? Color.red : UIManager.getColor( "Table.gridColor" ) ); table1.setGridColor( redGridColorCheckBox.isSelected() ? Color.red : UIManager.getColor( "Table.gridColor" ) );
} }
@Override private void showHorizontalLinesPropertyChange() {
public void updateUI() { showHorizontalLinesCheckBox.setSelected( table1.getShowHorizontalLines() );
super.updateUI(); }
EventQueue.invokeLater( () -> { private void showVerticalLinesPropertyChange() {
showHorizontalLinesChanged(); showVerticalLinesCheckBox.setSelected( table1.getShowVerticalLines() );
showVerticalLinesChanged(); }
intercellSpacingChanged();
} ); private void intercellSpacingPropertyChange() {
intercellSpacingCheckBox.setSelected( table1.getRowMargin() != 0 );
} }
@SuppressWarnings( { "unchecked", "rawtypes" } ) @SuppressWarnings( { "unchecked", "rawtypes" } )
@@ -333,10 +333,10 @@ class DataComponentsPanel
"Not editable", "Text", "Combo", "Combo Editable", "Integer", "Boolean" "Not editable", "Text", "Combo", "Combo Editable", "Integer", "Boolean"
} }
) { ) {
Class<?>[] columnTypes = new Class<?>[] { Class<?>[] columnTypes = {
Object.class, Object.class, String.class, String.class, Integer.class, Boolean.class Object.class, Object.class, String.class, String.class, Integer.class, Boolean.class
}; };
boolean[] columnEditable = new boolean[] { boolean[] columnEditable = {
false, true, true, true, true, true false, true, true, true, true, true
}; };
@Override @Override
@@ -383,6 +383,9 @@ class DataComponentsPanel
} }
table1.setAutoCreateRowSorter(true); table1.setAutoCreateRowSorter(true);
table1.setComponentPopupMenu(popupMenu2); table1.setComponentPopupMenu(popupMenu2);
table1.addPropertyChangeListener("showHorizontalLines", e -> showHorizontalLinesPropertyChange());
table1.addPropertyChangeListener("showVerticalLines", e -> showVerticalLinesPropertyChange());
table1.addPropertyChangeListener("rowMargin", e -> intercellSpacingPropertyChange());
scrollPane5.setViewportView(table1); scrollPane5.setViewportView(table1);
} }
add(scrollPane5, "cell 1 3 3 1,width 300"); add(scrollPane5, "cell 1 3 3 1,width 300");

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.0.0.0.194" Java: "17.0.2" encoding: "UTF-8" JFDML JFormDesigner: "8.2.0.0.331" Java: "21" encoding: "UTF-8"
new FormModel { new FormModel {
contentType: "form/swing" contentType: "form/swing"
@@ -333,6 +333,9 @@ new FormModel {
auxiliary() { auxiliary() {
"JavaCodeGenerator.variableLocal": false "JavaCodeGenerator.variableLocal": false
} }
addEvent( new FormEvent( "java.beans.PropertyChangeListener", "propertyChange", "showHorizontalLinesPropertyChange", false, "showHorizontalLines" ) )
addEvent( new FormEvent( "java.beans.PropertyChangeListener", "propertyChange", "showVerticalLinesPropertyChange", false, "showVerticalLines" ) )
addEvent( new FormEvent( "java.beans.PropertyChangeListener", "propertyChange", "intercellSpacingPropertyChange", false, "rowMargin" ) )
} ) } )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3 3 1,width 300" "value": "cell 1 3 3 1,width 300"

View File

@@ -46,7 +46,6 @@ import com.formdev.flatlaf.icons.FlatAbstractIcon;
import com.formdev.flatlaf.themes.FlatMacDarkLaf; import com.formdev.flatlaf.themes.FlatMacDarkLaf;
import com.formdev.flatlaf.themes.FlatMacLightLaf; import com.formdev.flatlaf.themes.FlatMacLightLaf;
import com.formdev.flatlaf.extras.FlatSVGUtils; import com.formdev.flatlaf.extras.FlatSVGUtils;
import com.formdev.flatlaf.ui.JBRCustomDecorations;
import com.formdev.flatlaf.util.ColorFunctions; import com.formdev.flatlaf.util.ColorFunctions;
import com.formdev.flatlaf.util.FontUtils; import com.formdev.flatlaf.util.FontUtils;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
@@ -931,12 +930,6 @@ class DemoFrame
menuBarEmbeddedCheckBoxMenuItem.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) ); menuBarEmbeddedCheckBoxMenuItem.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) );
unifiedTitleBarMenuItem.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) ); unifiedTitleBarMenuItem.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) );
showTitleBarIconMenuItem.setSelected( UIManager.getBoolean( "TitlePane.showIcon" ) ); showTitleBarIconMenuItem.setSelected( UIManager.getBoolean( "TitlePane.showIcon" ) );
if( JBRCustomDecorations.isSupported() ) {
// If the JetBrains Runtime is used, it forces the use of it's own custom
// window decoration, which can not disabled.
windowDecorationsCheckBoxMenuItem.setEnabled( false );
}
} else { } else {
unsupported( windowDecorationsCheckBoxMenuItem ); unsupported( windowDecorationsCheckBoxMenuItem );
unsupported( menuBarEmbeddedCheckBoxMenuItem ); unsupported( menuBarEmbeddedCheckBoxMenuItem );

View File

@@ -34,7 +34,7 @@ public class ScrollablePanel
{ {
@Override @Override
public Dimension getPreferredScrollableViewportSize() { public Dimension getPreferredScrollableViewportSize() {
return UIScale.scale( new Dimension( 400, 400 ) ); return new Dimension( getPreferredSize().width, UIScale.scale( 400 ) );
} }
@Override @Override
@@ -49,7 +49,7 @@ public class ScrollablePanel
@Override @Override
public boolean getScrollableTracksViewportWidth() { public boolean getScrollableTracksViewportWidth() {
return false; return true;
} }
@Override @Override

View File

@@ -204,7 +204,7 @@ class TabsPanel
private void closeButtonStyleChanged() { private void closeButtonStyleChanged() {
// WARNING: // WARNING:
// Do not use this trick to style individual tabbed panes in own code. // Do not use this trick to style individual tabbed panes in own code.
// Instead use one styling for all tabbed panes in your application. // Instead, use one styling for all tabbed panes in your application.
if( circleCloseButton.isSelected() ) { if( circleCloseButton.isSelected() ) {
UIManager.put( "TabbedPane.closeArc", 999 ); UIManager.put( "TabbedPane.closeArc", 999 );
UIManager.put( "TabbedPane.closeCrossFilledSize", 5.5f ); UIManager.put( "TabbedPane.closeCrossFilledSize", 5.5f );
@@ -313,6 +313,14 @@ class TabsPanel
putTabbedPanesClientProperty( TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators ); putTabbedPanesClientProperty( TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators );
} }
private void tabRotationChanged() {
String tabRotation = rotationAutoButton.isSelected() ? TABBED_PANE_TAB_ROTATION_AUTO
: rotationLeftButton.isSelected() ? TABBED_PANE_TAB_ROTATION_LEFT
: rotationRightButton.isSelected() ? TABBED_PANE_TAB_ROTATION_RIGHT
: null;
putTabbedPanesClientProperty( TABBED_PANE_TAB_ROTATION, tabRotation );
}
private void putTabbedPanesClientProperty( String key, Object value ) { private void putTabbedPanesClientProperty( String key, Object value ) {
updateTabbedPanesRecur( this, tabbedPane -> tabbedPane.putClientProperty( key, value ) ); updateTabbedPanesRecur( this, tabbedPane -> tabbedPane.putClientProperty( key, value ) );
} }
@@ -331,6 +339,8 @@ class TabsPanel
private void initComponents() { private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JScrollPane tabsScrollPane = new JScrollPane();
ScrollablePanel panel6 = new ScrollablePanel();
JPanel panel1 = new JPanel(); JPanel panel1 = new JPanel();
JLabel tabPlacementLabel = new JLabel(); JLabel tabPlacementLabel = new JLabel();
tabPlacementToolBar = new JToolBar(); tabPlacementToolBar = new JToolBar();
@@ -397,344 +407,369 @@ class TabsPanel
scrollAsNeededSingleButton = new JToggleButton(); scrollAsNeededSingleButton = new JToggleButton();
scrollAsNeededButton = new JToggleButton(); scrollAsNeededButton = new JToggleButton();
scrollNeverButton = new JToggleButton(); scrollNeverButton = new JToggleButton();
scrollButtonsPlacementLabel = new JLabel();
scrollButtonsPlacementToolBar = new JToolBar();
scrollBothButton = new JToggleButton();
scrollTrailingButton = new JToggleButton();
showTabSeparatorsCheckBox = new JCheckBox();
tabsPopupPolicyLabel = new JLabel(); tabsPopupPolicyLabel = new JLabel();
tabsPopupPolicyToolBar = new JToolBar(); tabsPopupPolicyToolBar = new JToolBar();
popupAsNeededButton = new JToggleButton(); popupAsNeededButton = new JToggleButton();
popupNeverButton = new JToggleButton(); popupNeverButton = new JToggleButton();
showTabSeparatorsCheckBox = new JCheckBox();
scrollButtonsPlacementLabel = new JLabel();
scrollButtonsPlacementToolBar = new JToolBar();
scrollBothButton = new JToggleButton();
scrollTrailingButton = new JToggleButton();
tabTypeLabel = new JLabel(); tabTypeLabel = new JLabel();
tabTypeToolBar = new JToolBar(); tabTypeToolBar = new JToolBar();
underlinedTabTypeButton = new JToggleButton(); underlinedTabTypeButton = new JToggleButton();
cardTabTypeButton = new JToggleButton(); cardTabTypeButton = new JToggleButton();
tabRotationLabel = new JLabel();
tabRotationToolBar = new JToolBar();
rotationNoneButton = new JToggleButton();
rotationAutoButton = new JToggleButton();
rotationLeftButton = new JToggleButton();
rotationRightButton = new JToggleButton();
//======== this ======== //======== this ========
setLayout(new MigLayout( setLayout(new MigLayout(
"insets dialog,hidemode 3", "insets 0,hidemode 3",
// columns // columns
"[grow,fill]para" + "[grow,fill]",
"[fill]para" +
"[fill]",
// rows // rows
"[grow,fill]para" + "[grow,fill]0" +
"[]" + "[]0" +
"[]")); "[]"));
//======== panel1 ======== //======== tabsScrollPane ========
{ {
panel1.setLayout(new MigLayout( tabsScrollPane.setBorder(BorderFactory.createEmptyBorder());
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]" +
"[fill]para" +
"[]0" +
"[]" +
"[]para" +
"[]" +
"[]para" +
"[]" +
"[]"));
//---- tabPlacementLabel ---- //======== panel6 ========
tabPlacementLabel.setText("Tab placement");
tabPlacementLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabPlacementLabel, "cell 0 0");
//======== tabPlacementToolBar ========
{ {
tabPlacementToolBar.setFloatable(false); panel6.setLayout(new MigLayout(
tabPlacementToolBar.setBorder(BorderFactory.createEmptyBorder()); "insets dialog,hidemode 3",
//---- topPlacementButton ----
topPlacementButton.setText("top");
topPlacementButton.setSelected(true);
topPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
topPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(topPlacementButton);
//---- bottomPlacementButton ----
bottomPlacementButton.setText("bottom");
bottomPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
bottomPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(bottomPlacementButton);
//---- leftPlacementButton ----
leftPlacementButton.setText("left");
leftPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
leftPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(leftPlacementButton);
//---- rightPlacementButton ----
rightPlacementButton.setText("right");
rightPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
rightPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(rightPlacementButton);
tabPlacementToolBar.addSeparator();
//---- scrollButton ----
scrollButton.setText("scroll");
scrollButton.putClientProperty("FlatLaf.styleClass", "small");
scrollButton.addActionListener(e -> scrollChanged());
tabPlacementToolBar.add(scrollButton);
//---- borderButton ----
borderButton.setText("border");
borderButton.putClientProperty("FlatLaf.styleClass", "small");
borderButton.addActionListener(e -> borderChanged());
tabPlacementToolBar.add(borderButton);
}
panel1.add(tabPlacementToolBar, "cell 0 0,alignx right,growx 0");
panel1.add(tabPlacementTabbedPane, "cell 0 1,width 300:300,height 100:100");
//---- tabLayoutLabel ----
tabLayoutLabel.setText("Tab layout");
tabLayoutLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabLayoutLabel, "cell 0 2");
//======== tabLayoutToolBar ========
{
tabLayoutToolBar.setFloatable(false);
tabLayoutToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- scrollTabLayoutButton ----
scrollTabLayoutButton.setText("scroll");
scrollTabLayoutButton.setSelected(true);
scrollTabLayoutButton.putClientProperty("FlatLaf.styleClass", "small");
scrollTabLayoutButton.addActionListener(e -> tabLayoutChanged());
tabLayoutToolBar.add(scrollTabLayoutButton);
//---- wrapTabLayoutButton ----
wrapTabLayoutButton.setText("wrap");
wrapTabLayoutButton.putClientProperty("FlatLaf.styleClass", "small");
wrapTabLayoutButton.addActionListener(e -> tabLayoutChanged());
tabLayoutToolBar.add(wrapTabLayoutButton);
}
panel1.add(tabLayoutToolBar, "cell 0 2,alignx right,growx 0");
//---- scrollLayoutNoteLabel ----
scrollLayoutNoteLabel.setText("(use mouse wheel to scroll; arrow button shows hidden tabs)");
scrollLayoutNoteLabel.setEnabled(false);
scrollLayoutNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel1.add(scrollLayoutNoteLabel, "cell 0 3");
//---- wrapLayoutNoteLabel ----
wrapLayoutNoteLabel.setText("(probably better to use scroll layout?)");
wrapLayoutNoteLabel.setEnabled(false);
wrapLayoutNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel1.add(wrapLayoutNoteLabel, "cell 0 3");
panel1.add(scrollLayoutTabbedPane, "cell 0 4");
panel1.add(wrapLayoutTabbedPane, "cell 0 4,width 100:100,height pref*2px");
//---- closableTabsLabel ----
closableTabsLabel.setText("Closable tabs");
closableTabsLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(closableTabsLabel, "cell 0 5");
//======== closableTabsToolBar ========
{
closableTabsToolBar.setFloatable(false);
closableTabsToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- squareCloseButton ----
squareCloseButton.setText("square");
squareCloseButton.setSelected(true);
squareCloseButton.putClientProperty("FlatLaf.styleClass", "small");
squareCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(squareCloseButton);
//---- circleCloseButton ----
circleCloseButton.setText("circle");
circleCloseButton.putClientProperty("FlatLaf.styleClass", "small");
circleCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(circleCloseButton);
//---- redCrossCloseButton ----
redCrossCloseButton.setText("red cross");
redCrossCloseButton.putClientProperty("FlatLaf.styleClass", "small");
redCrossCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(redCrossCloseButton);
}
panel1.add(closableTabsToolBar, "cell 0 5,alignx right,growx 0");
panel1.add(closableTabsTabbedPane, "cell 0 6");
//---- tabAreaComponentsLabel ----
tabAreaComponentsLabel.setText("Custom tab area components");
tabAreaComponentsLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabAreaComponentsLabel, "cell 0 7");
//======== tabAreaComponentsToolBar ========
{
tabAreaComponentsToolBar.setFloatable(false);
tabAreaComponentsToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- leadingComponentButton ----
leadingComponentButton.setText("leading");
leadingComponentButton.setSelected(true);
leadingComponentButton.putClientProperty("FlatLaf.styleClass", "small");
leadingComponentButton.addActionListener(e -> customComponentsChanged());
tabAreaComponentsToolBar.add(leadingComponentButton);
//---- trailingComponentButton ----
trailingComponentButton.setText("trailing");
trailingComponentButton.setSelected(true);
trailingComponentButton.putClientProperty("FlatLaf.styleClass", "small");
trailingComponentButton.addActionListener(e -> customComponentsChanged());
tabAreaComponentsToolBar.add(trailingComponentButton);
}
panel1.add(tabAreaComponentsToolBar, "cell 0 7,alignx right,growx 0");
panel1.add(customComponentsTabbedPane, "cell 0 8");
}
add(panel1, "cell 0 0");
//======== panel2 ========
{
panel2.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]0" +
"[]" +
"[fill]" +
"[center]" +
"[center]" +
"[center]para" +
"[center]0" +
"[]" +
"[center]" +
"[center]" +
"[center]" +
"[]"));
//---- tabIconPlacementLabel ----
tabIconPlacementLabel.setText("Tab icon placement");
tabIconPlacementLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel2.add(tabIconPlacementLabel, "cell 0 0");
//---- tabIconPlacementNodeLabel ----
tabIconPlacementNodeLabel.setText("(top/bottom/leading/trailing)");
tabIconPlacementNodeLabel.setEnabled(false);
tabIconPlacementNodeLabel.putClientProperty("FlatLaf.styleClass", "small");
panel2.add(tabIconPlacementNodeLabel, "cell 0 1");
panel2.add(iconTopTabbedPane, "cell 0 2");
panel2.add(iconBottomTabbedPane, "cell 0 3");
panel2.add(iconLeadingTabbedPane, "cell 0 4");
panel2.add(iconTrailingTabbedPane, "cell 0 5");
//---- tabAreaAlignmentLabel ----
tabAreaAlignmentLabel.setText("Tab area alignment");
tabAreaAlignmentLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel2.add(tabAreaAlignmentLabel, "cell 0 6");
//---- tabAreaAlignmentNoteLabel ----
tabAreaAlignmentNoteLabel.setText("(leading/center/trailing/fill)");
tabAreaAlignmentNoteLabel.setEnabled(false);
tabAreaAlignmentNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel2.add(tabAreaAlignmentNoteLabel, "cell 0 7");
panel2.add(alignLeadingTabbedPane, "cell 0 8");
panel2.add(alignCenterTabbedPane, "cell 0 9");
panel2.add(alignTrailingTabbedPane, "cell 0 10");
panel2.add(alignFillTabbedPane, "cell 0 11");
}
add(panel2, "cell 1 0,growy");
//======== panel3 ========
{
panel3.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]0" +
"[]" +
"[]" +
"[]" +
"[]para" +
"[]" +
"[]" +
"[]para" +
"[]0" +
"[]"));
//---- tabWidthModeLabel ----
tabWidthModeLabel.setText("Tab width mode");
tabWidthModeLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(tabWidthModeLabel, "cell 0 0");
//---- tabWidthModeNoteLabel ----
tabWidthModeNoteLabel.setText("(preferred/equal/compact)");
tabWidthModeNoteLabel.setEnabled(false);
tabWidthModeNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel3.add(tabWidthModeNoteLabel, "cell 0 1");
panel3.add(widthPreferredTabbedPane, "cell 0 2");
panel3.add(widthEqualTabbedPane, "cell 0 3");
panel3.add(widthCompactTabbedPane, "cell 0 4");
//---- minMaxTabWidthLabel ----
minMaxTabWidthLabel.setText("Minimum/maximum tab width");
minMaxTabWidthLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(minMaxTabWidthLabel, "cell 0 5");
panel3.add(minimumTabWidthTabbedPane, "cell 0 6");
panel3.add(maximumTabWidthTabbedPane, "cell 0 7");
//---- tabAlignmentLabel ----
tabAlignmentLabel.setText("Tab title alignment");
tabAlignmentLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(tabAlignmentLabel, "cell 0 8");
//======== panel5 ========
{
panel5.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns // columns
"[grow,fill]para" + "[grow,fill]para" +
"[fill]para" +
"[fill]", "[fill]",
// rows // rows
"[]" + "[grow,fill]"));
"[]" +
"[]" +
"[]"));
//---- tabAlignmentNoteLabel ---- //======== panel1 ========
tabAlignmentNoteLabel.setText("(leading/center/trailing)");
tabAlignmentNoteLabel.setEnabled(false);
tabAlignmentNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel5.add(tabAlignmentNoteLabel, "cell 0 0");
//---- tabAlignmentNoteLabel2 ----
tabAlignmentNoteLabel2.setText("(trailing)");
tabAlignmentNoteLabel2.setEnabled(false);
tabAlignmentNoteLabel2.putClientProperty("FlatLaf.styleClass", "small");
panel5.add(tabAlignmentNoteLabel2, "cell 1 0,alignx right,growx 0");
panel5.add(tabAlignLeadingTabbedPane, "cell 0 1");
//======== tabAlignVerticalTabbedPane ========
{ {
tabAlignVerticalTabbedPane.setTabPlacement(SwingConstants.LEFT); panel1.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]" +
"[fill]para" +
"[]0" +
"[]" +
"[]para" +
"[]" +
"[]para" +
"[]" +
"[]"));
//---- tabPlacementLabel ----
tabPlacementLabel.setText("Tab placement");
tabPlacementLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabPlacementLabel, "cell 0 0");
//======== tabPlacementToolBar ========
{
tabPlacementToolBar.setFloatable(false);
tabPlacementToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- topPlacementButton ----
topPlacementButton.setText("top");
topPlacementButton.setSelected(true);
topPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
topPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(topPlacementButton);
//---- bottomPlacementButton ----
bottomPlacementButton.setText("bottom");
bottomPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
bottomPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(bottomPlacementButton);
//---- leftPlacementButton ----
leftPlacementButton.setText("left");
leftPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
leftPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(leftPlacementButton);
//---- rightPlacementButton ----
rightPlacementButton.setText("right");
rightPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
rightPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(rightPlacementButton);
tabPlacementToolBar.addSeparator();
//---- scrollButton ----
scrollButton.setText("scroll");
scrollButton.putClientProperty("FlatLaf.styleClass", "small");
scrollButton.addActionListener(e -> scrollChanged());
tabPlacementToolBar.add(scrollButton);
//---- borderButton ----
borderButton.setText("border");
borderButton.putClientProperty("FlatLaf.styleClass", "small");
borderButton.addActionListener(e -> borderChanged());
tabPlacementToolBar.add(borderButton);
}
panel1.add(tabPlacementToolBar, "cell 0 0,alignx right,growx 0");
panel1.add(tabPlacementTabbedPane, "cell 0 1,width 300:300,height 100:100");
//---- tabLayoutLabel ----
tabLayoutLabel.setText("Tab layout");
tabLayoutLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabLayoutLabel, "cell 0 2");
//======== tabLayoutToolBar ========
{
tabLayoutToolBar.setFloatable(false);
tabLayoutToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- scrollTabLayoutButton ----
scrollTabLayoutButton.setText("scroll");
scrollTabLayoutButton.setSelected(true);
scrollTabLayoutButton.putClientProperty("FlatLaf.styleClass", "small");
scrollTabLayoutButton.addActionListener(e -> tabLayoutChanged());
tabLayoutToolBar.add(scrollTabLayoutButton);
//---- wrapTabLayoutButton ----
wrapTabLayoutButton.setText("wrap");
wrapTabLayoutButton.putClientProperty("FlatLaf.styleClass", "small");
wrapTabLayoutButton.addActionListener(e -> tabLayoutChanged());
tabLayoutToolBar.add(wrapTabLayoutButton);
}
panel1.add(tabLayoutToolBar, "cell 0 2,alignx right,growx 0");
//---- scrollLayoutNoteLabel ----
scrollLayoutNoteLabel.setText("(use mouse wheel to scroll; arrow button shows hidden tabs)");
scrollLayoutNoteLabel.setEnabled(false);
scrollLayoutNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel1.add(scrollLayoutNoteLabel, "cell 0 3");
//---- wrapLayoutNoteLabel ----
wrapLayoutNoteLabel.setText("(probably better to use scroll layout?)");
wrapLayoutNoteLabel.setEnabled(false);
wrapLayoutNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel1.add(wrapLayoutNoteLabel, "cell 0 3");
panel1.add(scrollLayoutTabbedPane, "cell 0 4");
panel1.add(wrapLayoutTabbedPane, "cell 0 4,width 100:100,height pref*2px");
//---- closableTabsLabel ----
closableTabsLabel.setText("Closable tabs");
closableTabsLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(closableTabsLabel, "cell 0 5");
//======== closableTabsToolBar ========
{
closableTabsToolBar.setFloatable(false);
closableTabsToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- squareCloseButton ----
squareCloseButton.setText("square");
squareCloseButton.setSelected(true);
squareCloseButton.putClientProperty("FlatLaf.styleClass", "small");
squareCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(squareCloseButton);
//---- circleCloseButton ----
circleCloseButton.setText("circle");
circleCloseButton.putClientProperty("FlatLaf.styleClass", "small");
circleCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(circleCloseButton);
//---- redCrossCloseButton ----
redCrossCloseButton.setText("red cross");
redCrossCloseButton.putClientProperty("FlatLaf.styleClass", "small");
redCrossCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(redCrossCloseButton);
}
panel1.add(closableTabsToolBar, "cell 0 5,alignx right,growx 0");
panel1.add(closableTabsTabbedPane, "cell 0 6");
//---- tabAreaComponentsLabel ----
tabAreaComponentsLabel.setText("Custom tab area components");
tabAreaComponentsLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabAreaComponentsLabel, "cell 0 7");
//======== tabAreaComponentsToolBar ========
{
tabAreaComponentsToolBar.setFloatable(false);
tabAreaComponentsToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- leadingComponentButton ----
leadingComponentButton.setText("leading");
leadingComponentButton.setSelected(true);
leadingComponentButton.putClientProperty("FlatLaf.styleClass", "small");
leadingComponentButton.addActionListener(e -> customComponentsChanged());
tabAreaComponentsToolBar.add(leadingComponentButton);
//---- trailingComponentButton ----
trailingComponentButton.setText("trailing");
trailingComponentButton.setSelected(true);
trailingComponentButton.putClientProperty("FlatLaf.styleClass", "small");
trailingComponentButton.addActionListener(e -> customComponentsChanged());
tabAreaComponentsToolBar.add(trailingComponentButton);
}
panel1.add(tabAreaComponentsToolBar, "cell 0 7,alignx right,growx 0");
panel1.add(customComponentsTabbedPane, "cell 0 8");
} }
panel5.add(tabAlignVerticalTabbedPane, "cell 1 1 1 3,growy"); panel6.add(panel1, "cell 0 0");
panel5.add(tabAlignCenterTabbedPane, "cell 0 2");
panel5.add(tabAlignTrailingTabbedPane, "cell 0 3"); //======== panel2 ========
{
panel2.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]0" +
"[]" +
"[fill]" +
"[center]" +
"[center]" +
"[center]para" +
"[center]0" +
"[]" +
"[center]" +
"[center]" +
"[center]" +
"[]"));
//---- tabIconPlacementLabel ----
tabIconPlacementLabel.setText("Tab icon placement");
tabIconPlacementLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel2.add(tabIconPlacementLabel, "cell 0 0");
//---- tabIconPlacementNodeLabel ----
tabIconPlacementNodeLabel.setText("(top/bottom/leading/trailing)");
tabIconPlacementNodeLabel.setEnabled(false);
tabIconPlacementNodeLabel.putClientProperty("FlatLaf.styleClass", "small");
panel2.add(tabIconPlacementNodeLabel, "cell 0 1");
panel2.add(iconTopTabbedPane, "cell 0 2");
panel2.add(iconBottomTabbedPane, "cell 0 3");
panel2.add(iconLeadingTabbedPane, "cell 0 4");
panel2.add(iconTrailingTabbedPane, "cell 0 5");
//---- tabAreaAlignmentLabel ----
tabAreaAlignmentLabel.setText("Tab area alignment");
tabAreaAlignmentLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel2.add(tabAreaAlignmentLabel, "cell 0 6");
//---- tabAreaAlignmentNoteLabel ----
tabAreaAlignmentNoteLabel.setText("(leading/center/trailing/fill)");
tabAreaAlignmentNoteLabel.setEnabled(false);
tabAreaAlignmentNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel2.add(tabAreaAlignmentNoteLabel, "cell 0 7");
panel2.add(alignLeadingTabbedPane, "cell 0 8");
panel2.add(alignCenterTabbedPane, "cell 0 9");
panel2.add(alignTrailingTabbedPane, "cell 0 10");
panel2.add(alignFillTabbedPane, "cell 0 11");
}
panel6.add(panel2, "cell 1 0,growy");
//======== panel3 ========
{
panel3.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]0" +
"[]" +
"[]" +
"[]" +
"[]para" +
"[]" +
"[]" +
"[]para" +
"[]0" +
"[]"));
//---- tabWidthModeLabel ----
tabWidthModeLabel.setText("Tab width mode");
tabWidthModeLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(tabWidthModeLabel, "cell 0 0");
//---- tabWidthModeNoteLabel ----
tabWidthModeNoteLabel.setText("(preferred/equal/compact)");
tabWidthModeNoteLabel.setEnabled(false);
tabWidthModeNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel3.add(tabWidthModeNoteLabel, "cell 0 1");
panel3.add(widthPreferredTabbedPane, "cell 0 2");
panel3.add(widthEqualTabbedPane, "cell 0 3");
panel3.add(widthCompactTabbedPane, "cell 0 4");
//---- minMaxTabWidthLabel ----
minMaxTabWidthLabel.setText("Minimum/maximum tab width");
minMaxTabWidthLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(minMaxTabWidthLabel, "cell 0 5");
panel3.add(minimumTabWidthTabbedPane, "cell 0 6");
panel3.add(maximumTabWidthTabbedPane, "cell 0 7");
//---- tabAlignmentLabel ----
tabAlignmentLabel.setText("Tab title alignment");
tabAlignmentLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(tabAlignmentLabel, "cell 0 8");
//======== panel5 ========
{
panel5.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]para" +
"[fill]",
// rows
"[]" +
"[]" +
"[]" +
"[]" +
"[]"));
//---- tabAlignmentNoteLabel ----
tabAlignmentNoteLabel.setText("(leading/center/trailing)");
tabAlignmentNoteLabel.setEnabled(false);
tabAlignmentNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel5.add(tabAlignmentNoteLabel, "cell 0 0");
//---- tabAlignmentNoteLabel2 ----
tabAlignmentNoteLabel2.setText("(trailing)");
tabAlignmentNoteLabel2.setEnabled(false);
tabAlignmentNoteLabel2.putClientProperty("FlatLaf.styleClass", "small");
panel5.add(tabAlignmentNoteLabel2, "cell 1 0,alignx right,growx 0");
panel5.add(tabAlignLeadingTabbedPane, "cell 0 1");
//======== tabAlignVerticalTabbedPane ========
{
tabAlignVerticalTabbedPane.setTabPlacement(SwingConstants.LEFT);
}
panel5.add(tabAlignVerticalTabbedPane, "cell 1 1 1 4,growy");
panel5.add(tabAlignCenterTabbedPane, "cell 0 2");
panel5.add(tabAlignTrailingTabbedPane, "cell 0 3");
}
panel3.add(panel5, "cell 0 9");
}
panel6.add(panel3, "cell 2 0");
} }
panel3.add(panel5, "cell 0 9"); tabsScrollPane.setViewportView(panel6);
} }
add(panel3, "cell 2 0"); add(tabsScrollPane, "cell 0 0");
add(separator2, "cell 0 1 3 1"); add(separator2, "cell 0 1");
//======== panel4 ======== //======== panel4 ========
{ {
panel4.setLayout(new MigLayout( panel4.setLayout(new MigLayout(
"insets 0,hidemode 3", "insets panel,hidemode 3",
// columns // columns
"[]" + "[]" +
"[fill]para" + "[fill]para" +
"[fill]" + "[fill]" +
"[fill]para" + "[fill]para" +
"[fill]" +
"[fill]", "[fill]",
// rows // rows
"[]" + "[]" +
@@ -770,38 +805,9 @@ class TabsPanel
} }
panel4.add(scrollButtonsPolicyToolBar, "cell 1 0"); panel4.add(scrollButtonsPolicyToolBar, "cell 1 0");
//---- scrollButtonsPlacementLabel ----
scrollButtonsPlacementLabel.setText("Scroll buttons placement:");
panel4.add(scrollButtonsPlacementLabel, "cell 2 0");
//======== scrollButtonsPlacementToolBar ========
{
scrollButtonsPlacementToolBar.setFloatable(false);
scrollButtonsPlacementToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- scrollBothButton ----
scrollBothButton.setText("both");
scrollBothButton.setSelected(true);
scrollBothButton.putClientProperty("FlatLaf.styleClass", "small");
scrollBothButton.addActionListener(e -> scrollButtonsPlacementChanged());
scrollButtonsPlacementToolBar.add(scrollBothButton);
//---- scrollTrailingButton ----
scrollTrailingButton.setText("trailing");
scrollTrailingButton.putClientProperty("FlatLaf.styleClass", "small");
scrollTrailingButton.addActionListener(e -> scrollButtonsPlacementChanged());
scrollButtonsPlacementToolBar.add(scrollTrailingButton);
}
panel4.add(scrollButtonsPlacementToolBar, "cell 3 0");
//---- showTabSeparatorsCheckBox ----
showTabSeparatorsCheckBox.setText("Show tab separators");
showTabSeparatorsCheckBox.addActionListener(e -> showTabSeparatorsChanged());
panel4.add(showTabSeparatorsCheckBox, "cell 4 0");
//---- tabsPopupPolicyLabel ---- //---- tabsPopupPolicyLabel ----
tabsPopupPolicyLabel.setText("Tabs popup policy:"); tabsPopupPolicyLabel.setText("Tabs popup policy:");
panel4.add(tabsPopupPolicyLabel, "cell 0 1"); panel4.add(tabsPopupPolicyLabel, "cell 2 0");
//======== tabsPopupPolicyToolBar ======== //======== tabsPopupPolicyToolBar ========
{ {
@@ -821,7 +827,36 @@ class TabsPanel
popupNeverButton.addActionListener(e -> tabsPopupPolicyChanged()); popupNeverButton.addActionListener(e -> tabsPopupPolicyChanged());
tabsPopupPolicyToolBar.add(popupNeverButton); tabsPopupPolicyToolBar.add(popupNeverButton);
} }
panel4.add(tabsPopupPolicyToolBar, "cell 1 1"); panel4.add(tabsPopupPolicyToolBar, "cell 3 0");
//---- showTabSeparatorsCheckBox ----
showTabSeparatorsCheckBox.setText("Show tab separators");
showTabSeparatorsCheckBox.addActionListener(e -> showTabSeparatorsChanged());
panel4.add(showTabSeparatorsCheckBox, "cell 4 0 2 1,alignx left,growx 0");
//---- scrollButtonsPlacementLabel ----
scrollButtonsPlacementLabel.setText("Scroll buttons placement:");
panel4.add(scrollButtonsPlacementLabel, "cell 0 1");
//======== scrollButtonsPlacementToolBar ========
{
scrollButtonsPlacementToolBar.setFloatable(false);
scrollButtonsPlacementToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- scrollBothButton ----
scrollBothButton.setText("both");
scrollBothButton.setSelected(true);
scrollBothButton.putClientProperty("FlatLaf.styleClass", "small");
scrollBothButton.addActionListener(e -> scrollButtonsPlacementChanged());
scrollButtonsPlacementToolBar.add(scrollBothButton);
//---- scrollTrailingButton ----
scrollTrailingButton.setText("trailing");
scrollTrailingButton.putClientProperty("FlatLaf.styleClass", "small");
scrollTrailingButton.addActionListener(e -> scrollButtonsPlacementChanged());
scrollButtonsPlacementToolBar.add(scrollTrailingButton);
}
panel4.add(scrollButtonsPlacementToolBar, "cell 1 1");
//---- tabTypeLabel ---- //---- tabTypeLabel ----
tabTypeLabel.setText("Tab type:"); tabTypeLabel.setText("Tab type:");
@@ -845,8 +880,44 @@ class TabsPanel
tabTypeToolBar.add(cardTabTypeButton); tabTypeToolBar.add(cardTabTypeButton);
} }
panel4.add(tabTypeToolBar, "cell 3 1"); panel4.add(tabTypeToolBar, "cell 3 1");
//---- tabRotationLabel ----
tabRotationLabel.setText("Tab rotation:");
panel4.add(tabRotationLabel, "cell 4 1");
//======== tabRotationToolBar ========
{
tabRotationToolBar.setFloatable(false);
tabRotationToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- rotationNoneButton ----
rotationNoneButton.setText("none");
rotationNoneButton.setSelected(true);
rotationNoneButton.putClientProperty("FlatLaf.styleClass", "small");
rotationNoneButton.addActionListener(e -> tabRotationChanged());
tabRotationToolBar.add(rotationNoneButton);
//---- rotationAutoButton ----
rotationAutoButton.setText("auto");
rotationAutoButton.putClientProperty("FlatLaf.styleClass", "small");
rotationAutoButton.addActionListener(e -> tabRotationChanged());
tabRotationToolBar.add(rotationAutoButton);
//---- rotationLeftButton ----
rotationLeftButton.setText("left");
rotationLeftButton.putClientProperty("FlatLaf.styleClass", "small");
rotationLeftButton.addActionListener(e -> tabRotationChanged());
tabRotationToolBar.add(rotationLeftButton);
//---- rotationRightButton ----
rotationRightButton.setText("right");
rotationRightButton.putClientProperty("FlatLaf.styleClass", "small");
rotationRightButton.addActionListener(e -> tabRotationChanged());
tabRotationToolBar.add(rotationRightButton);
}
panel4.add(tabRotationToolBar, "cell 5 1");
} }
add(panel4, "cell 0 2 3 1"); add(panel4, "cell 0 2");
//---- tabPlacementButtonGroup ---- //---- tabPlacementButtonGroup ----
ButtonGroup tabPlacementButtonGroup = new ButtonGroup(); ButtonGroup tabPlacementButtonGroup = new ButtonGroup();
@@ -872,20 +943,27 @@ class TabsPanel
scrollButtonsPolicyButtonGroup.add(scrollAsNeededButton); scrollButtonsPolicyButtonGroup.add(scrollAsNeededButton);
scrollButtonsPolicyButtonGroup.add(scrollNeverButton); scrollButtonsPolicyButtonGroup.add(scrollNeverButton);
//---- scrollButtonsPlacementButtonGroup ----
ButtonGroup scrollButtonsPlacementButtonGroup = new ButtonGroup();
scrollButtonsPlacementButtonGroup.add(scrollBothButton);
scrollButtonsPlacementButtonGroup.add(scrollTrailingButton);
//---- tabsPopupPolicyButtonGroup ---- //---- tabsPopupPolicyButtonGroup ----
ButtonGroup tabsPopupPolicyButtonGroup = new ButtonGroup(); ButtonGroup tabsPopupPolicyButtonGroup = new ButtonGroup();
tabsPopupPolicyButtonGroup.add(popupAsNeededButton); tabsPopupPolicyButtonGroup.add(popupAsNeededButton);
tabsPopupPolicyButtonGroup.add(popupNeverButton); tabsPopupPolicyButtonGroup.add(popupNeverButton);
//---- scrollButtonsPlacementButtonGroup ----
ButtonGroup scrollButtonsPlacementButtonGroup = new ButtonGroup();
scrollButtonsPlacementButtonGroup.add(scrollBothButton);
scrollButtonsPlacementButtonGroup.add(scrollTrailingButton);
//---- tabTypeButtonGroup ---- //---- tabTypeButtonGroup ----
ButtonGroup tabTypeButtonGroup = new ButtonGroup(); ButtonGroup tabTypeButtonGroup = new ButtonGroup();
tabTypeButtonGroup.add(underlinedTabTypeButton); tabTypeButtonGroup.add(underlinedTabTypeButton);
tabTypeButtonGroup.add(cardTabTypeButton); tabTypeButtonGroup.add(cardTabTypeButton);
//---- tabRotationButtonGroup ----
ButtonGroup tabRotationButtonGroup = new ButtonGroup();
tabRotationButtonGroup.add(rotationNoneButton);
tabRotationButtonGroup.add(rotationAutoButton);
tabRotationButtonGroup.add(rotationLeftButton);
tabRotationButtonGroup.add(rotationRightButton);
// JFormDesigner - End of component initialization //GEN-END:initComponents // JFormDesigner - End of component initialization //GEN-END:initComponents
if( FlatLafDemo.screenshotsMode ) { if( FlatLafDemo.screenshotsMode ) {
@@ -961,18 +1039,24 @@ class TabsPanel
private JToggleButton scrollAsNeededSingleButton; private JToggleButton scrollAsNeededSingleButton;
private JToggleButton scrollAsNeededButton; private JToggleButton scrollAsNeededButton;
private JToggleButton scrollNeverButton; private JToggleButton scrollNeverButton;
private JLabel scrollButtonsPlacementLabel;
private JToolBar scrollButtonsPlacementToolBar;
private JToggleButton scrollBothButton;
private JToggleButton scrollTrailingButton;
private JCheckBox showTabSeparatorsCheckBox;
private JLabel tabsPopupPolicyLabel; private JLabel tabsPopupPolicyLabel;
private JToolBar tabsPopupPolicyToolBar; private JToolBar tabsPopupPolicyToolBar;
private JToggleButton popupAsNeededButton; private JToggleButton popupAsNeededButton;
private JToggleButton popupNeverButton; private JToggleButton popupNeverButton;
private JCheckBox showTabSeparatorsCheckBox;
private JLabel scrollButtonsPlacementLabel;
private JToolBar scrollButtonsPlacementToolBar;
private JToggleButton scrollBothButton;
private JToggleButton scrollTrailingButton;
private JLabel tabTypeLabel; private JLabel tabTypeLabel;
private JToolBar tabTypeToolBar; private JToolBar tabTypeToolBar;
private JToggleButton underlinedTabTypeButton; private JToggleButton underlinedTabTypeButton;
private JToggleButton cardTabTypeButton; private JToggleButton cardTabTypeButton;
private JLabel tabRotationLabel;
private JToolBar tabRotationToolBar;
private JToggleButton rotationNoneButton;
private JToggleButton rotationAutoButton;
private JToggleButton rotationLeftButton;
private JToggleButton rotationRightButton;
// JFormDesigner - End of variables declaration //GEN-END:variables // JFormDesigner - End of variables declaration //GEN-END:variables
} }

View File

@@ -33,7 +33,7 @@ build script:
artifactId: flatlaf-extras artifactId: flatlaf-extras
version: (see button below) version: (see button below)
Otherwise download `flatlaf-extras-<version>.jar` here: Otherwise, download `flatlaf-extras-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-extras/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-extras) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-extras/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-extras)

View File

@@ -133,8 +133,8 @@ public class FlatAnimatedLafChange
} }
/** /**
* Starts an animation that shows the snapshot (created by {@link #showSnapshot()} * Starts an animation that shows the snapshot (created by {@link #showSnapshot()})
* with an decreasing alpha. At the end, the snapshot is removed and the new UI is shown. * with a decreasing alpha. At the end, the snapshot is removed and the new UI is shown.
* Invoke after updating UI. * Invoke after updating UI.
*/ */
public static void hideSnapshotWithAnimation() { public static void hideSnapshotWithAnimation() {

View File

@@ -119,7 +119,7 @@ public class FlatDesktop
(proxy, method, args) -> { (proxy, method, args) -> {
// Use invokeLater to release the listener firing for the case // Use invokeLater to release the listener firing for the case
// that the action listener shows a modal dialog. // that the action listener shows a modal dialog.
// This (hopefully) prevents application hunging. // This (hopefully) prevents application hanging.
EventQueue.invokeLater( () -> { EventQueue.invokeLater( () -> {
handler.run(); handler.run();
} ); } );

View File

@@ -87,7 +87,7 @@ public class FlatSVGIcon
* in the tag {@code <svg>} are used as icon size. * in the tag {@code <svg>} are used as icon size.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}. * Otherwise, use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -106,7 +106,7 @@ public class FlatSVGIcon
* in the tag {@code <svg>} are used as icon size. * in the tag {@code <svg>} are used as icon size.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}. * Otherwise, use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -124,7 +124,7 @@ public class FlatSVGIcon
* The icon is scaled if the given size is different to the size specified in the SVG file. * The icon is scaled if the given size is different to the size specified in the SVG file.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}. * Otherwise, use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -144,7 +144,7 @@ public class FlatSVGIcon
* The icon is scaled if the given size is different to the size specified in the SVG file. * The icon is scaled if the given size is different to the size specified in the SVG file.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}. * Otherwise, use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -166,7 +166,7 @@ public class FlatSVGIcon
* by the given scale factor. * by the given scale factor.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}. * Otherwise, use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -187,7 +187,7 @@ public class FlatSVGIcon
* by the given scale factor. * by the given scale factor.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}. * Otherwise, use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -259,7 +259,7 @@ public class FlatSVGIcon
* <p> * <p>
* The input stream is loaded, parsed and closed immediately. * The input stream is loaded, parsed and closed immediately.
* *
* @param in the input stream for reading a SVG resource * @param in the input stream for reading an SVG resource
* @throws IOException if an I/O exception occurs * @throws IOException if an I/O exception occurs
* @since 2 * @since 2
*/ */

View File

@@ -46,10 +46,10 @@ public class FlatSVGUtils
* then a single multi-resolution image is returned that creates images on demand * then a single multi-resolution image is returned that creates images on demand
* for requested sizes from SVG. * for requested sizes from SVG.
* This has the advantage that only images for used sizes are created. * This has the advantage that only images for used sizes are created.
* Also if unusual sizes are requested (e.g. 18x18), then they are created from SVG. * Also, if unusual sizes are requested (e.g. 18x18), then they are created from SVG.
* <p> * <p>
* If using Java modules, the package containing the SVG must be opened in {@code module-info.java}. * If using Java modules, the package containing the SVG must be opened in {@code module-info.java}.
* Otherwise use {@link #createWindowIconImages(URL)}. * Otherwise, use {@link #createWindowIconImages(URL)}.
* *
* @param svgName the name of the SVG resource (a '/'-separated path) * @param svgName the name of the SVG resource (a '/'-separated path)
* @return list of icon images with different sizes (16x16, 20x20, 24x24, 28x28, 32x32, 48x48 and 64x64) * @return list of icon images with different sizes (16x16, 20x20, 24x24, 28x28, 32x32, 48x48 and 64x64)
@@ -69,7 +69,7 @@ public class FlatSVGUtils
* then a single multi-resolution image is returned that creates images on demand * then a single multi-resolution image is returned that creates images on demand
* for requested sizes from SVG. * for requested sizes from SVG.
* This has the advantage that only images for used sizes are created. * This has the advantage that only images for used sizes are created.
* Also if unusual sizes are requested (e.g. 18x18), then they are created from SVG. * Also, if unusual sizes are requested (e.g. 18x18), then they are created from SVG.
* <p> * <p>
* This method is useful if using Java modules and the package containing the SVG * This method is useful if using Java modules and the package containing the SVG
* is not opened in {@code module-info.java}. * is not opened in {@code module-info.java}.
@@ -92,7 +92,7 @@ public class FlatSVGUtils
// any size is created on demand when // any size is created on demand when
// MultiResolutionImage.getResolutionVariant(double destImageWidth, double destImageHeight) // MultiResolutionImage.getResolutionVariant(double destImageWidth, double destImageHeight)
// is invoked. // is invoked.
// This sizes are only used by MultiResolutionImage.getResolutionVariants(). // These sizes are only used by MultiResolutionImage.getResolutionVariants().
new Dimension( 16, 16 ), // 100% new Dimension( 16, 16 ), // 100%
new Dimension( 20, 20 ), // 125% new Dimension( 20, 20 ), // 125%
new Dimension( 24, 24 ), // 150% new Dimension( 24, 24 ), // 150%
@@ -120,7 +120,7 @@ public class FlatSVGUtils
* Creates a buffered image and renders the given SVG into it. * Creates a buffered image and renders the given SVG into it.
* <p> * <p>
* If using Java modules, the package containing the SVG must be opened in {@code module-info.java}. * If using Java modules, the package containing the SVG must be opened in {@code module-info.java}.
* Otherwise use {@link #svg2image(URL, int, int)}. * Otherwise, use {@link #svg2image(URL, int, int)}.
* *
* @param svgName the name of the SVG resource (a '/'-separated path) * @param svgName the name of the SVG resource (a '/'-separated path)
* @param width the width of the image * @param width the width of the image
@@ -154,7 +154,7 @@ public class FlatSVGUtils
* Creates a buffered image and renders the given SVG into it. * Creates a buffered image and renders the given SVG into it.
* <p> * <p>
* If using Java modules, the package containing the SVG must be opened in {@code module-info.java}. * If using Java modules, the package containing the SVG must be opened in {@code module-info.java}.
* Otherwise use {@link #svg2image(URL, float)}. * Otherwise, use {@link #svg2image(URL, float)}.
* *
* @param svgName the name of the SVG resource (a '/'-separated path) * @param svgName the name of the SVG resource (a '/'-separated path)
* @param scaleFactor the amount by which the SVG size is scaled * @param scaleFactor the amount by which the SVG size is scaled

View File

@@ -557,7 +557,7 @@ public class FlatUIDefaultsInspector
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
panel = new JPanel(); panel = new JPanel();
filterPanel = new JPanel(); filterPanel = new JPanel();
flterLabel = new JLabel(); filterLabel = new JLabel();
filterField = new FlatTextField(); filterField = new FlatTextField();
valueTypeLabel = new JLabel(); valueTypeLabel = new JLabel();
valueTypeField = new JComboBox<>(); valueTypeField = new JComboBox<>();
@@ -580,11 +580,11 @@ public class FlatUIDefaultsInspector
((GridBagLayout)filterPanel.getLayout()).columnWeights = new double[] {0.0, 1.0, 0.0, 0.0, 1.0E-4}; ((GridBagLayout)filterPanel.getLayout()).columnWeights = new double[] {0.0, 1.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout)filterPanel.getLayout()).rowWeights = new double[] {0.0, 1.0E-4}; ((GridBagLayout)filterPanel.getLayout()).rowWeights = new double[] {0.0, 1.0E-4};
//---- flterLabel ---- //---- filterLabel ----
flterLabel.setText("Filter:"); filterLabel.setText("Filter:");
flterLabel.setLabelFor(filterField); filterLabel.setLabelFor(filterField);
flterLabel.setDisplayedMnemonic('F'); filterLabel.setDisplayedMnemonic('F');
filterPanel.add(flterLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, filterPanel.add(filterLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 10), 0, 0)); new Insets(0, 0, 0, 10), 0, 0));
@@ -668,7 +668,7 @@ public class FlatUIDefaultsInspector
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JPanel panel; private JPanel panel;
private JPanel filterPanel; private JPanel filterPanel;
private JLabel flterLabel; private JLabel filterLabel;
private FlatTextField filterField; private FlatTextField filterField;
private JLabel valueTypeLabel; private JLabel valueTypeLabel;
private JComboBox<String> valueTypeField; private JComboBox<String> valueTypeField;

View File

@@ -15,7 +15,7 @@ new FormModel {
} ) { } ) {
name: "filterPanel" name: "filterPanel"
add( new FormComponent( "javax.swing.JLabel" ) { add( new FormComponent( "javax.swing.JLabel" ) {
name: "flterLabel" name: "filterLabel"
"text": "Filter:" "text": "Filter:"
"labelFor": new FormReference( "filterField" ) "labelFor": new FormReference( "filterField" )
"displayedMnemonic": 70 "displayedMnemonic": 70

View File

@@ -33,14 +33,14 @@ public class FlatButton
public enum ButtonType { none, square, roundRect, tab, help, toolBarButton, borderless } public enum ButtonType { none, square, roundRect, tab, help, toolBarButton, borderless }
/** /**
* Returns type of a button. * Returns type of button.
*/ */
public ButtonType getButtonType() { public ButtonType getButtonType() {
return getClientPropertyEnumString( BUTTON_TYPE, ButtonType.class, null, ButtonType.none ); return getClientPropertyEnumString( BUTTON_TYPE, ButtonType.class, null, ButtonType.none );
} }
/** /**
* Specifies type of a button. * Specifies type of button.
*/ */
public void setButtonType( ButtonType buttonType ) { public void setButtonType( ButtonType buttonType ) {
if( buttonType == ButtonType.none ) if( buttonType == ButtonType.none )

View File

@@ -106,7 +106,7 @@ public class FlatFormattedTextField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}. * (see {@link #setOutline(Object)}).
* *
* @since 2 * @since 2
*/ */
@@ -135,7 +135,7 @@ public class FlatFormattedTextField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}. * (see {@link #setOutline(Object)}).
* *
* @since 2 * @since 2
*/ */

View File

@@ -106,7 +106,7 @@ public class FlatPasswordField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}. * (see {@link #setOutline(Object)}).
* *
* @since 2 * @since 2
*/ */
@@ -135,7 +135,7 @@ public class FlatPasswordField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}. * (see {@link #setOutline(Object)}).
* *
* @since 2 * @since 2
*/ */

View File

@@ -502,6 +502,29 @@ public class FlatTabbedPane
} }
// NOTE: enum names must be equal to allowed strings
/** @since 3.3 */ public enum TabRotation { none, auto, left, right }
/**
* Returns how the tabs should be rotated.
*
* @since 3.3
*/
public TabRotation getTabRotation() {
return getClientPropertyEnumString( TABBED_PANE_TAB_ROTATION, TabRotation.class,
"TabbedPane.tabRotation", TabRotation.none );
}
/**
* Specifies how the tabs should be rotated.
*
* @since 3.3
*/
public void setTabRotation( TabRotation tabRotation ) {
putClientPropertyEnumString( TABBED_PANE_TAB_ROTATION, tabRotation );
}
/** /**
* Returns the tab icon placement (relative to tab title). * Returns the tab icon placement (relative to tab title).
*/ */

View File

@@ -105,7 +105,7 @@ public class FlatTextField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}. * (see {@link #setOutline(Object)}).
* *
* @since 2 * @since 2
*/ */
@@ -134,7 +134,7 @@ public class FlatTextField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}. * (see {@link #setOutline(Object)}).
* *
* @since 2 * @since 2
*/ */

View File

@@ -31,14 +31,14 @@ public class FlatToggleButton
implements FlatComponentExtension, FlatStyleableComponent implements FlatComponentExtension, FlatStyleableComponent
{ {
/** /**
* Returns type of a button. * Returns type of button.
*/ */
public ButtonType getButtonType() { public ButtonType getButtonType() {
return getClientPropertyEnumString( BUTTON_TYPE, ButtonType.class, null, ButtonType.none ); return getClientPropertyEnumString( BUTTON_TYPE, ButtonType.class, null, ButtonType.none );
} }
/** /**
* Specifies type of a button. * Specifies type of button.
*/ */
public void setButtonType( ButtonType buttonType ) { public void setButtonType( ButtonType buttonType ) {
if( buttonType == ButtonType.none ) if( buttonType == ButtonType.none )

View File

@@ -30,8 +30,8 @@ import com.formdev.flatlaf.FlatLaf;
* <p> * <p>
* The initial state is {@link State#INDETERMINATE}. * The initial state is {@link State#INDETERMINATE}.
* <p> * <p>
* By default the third state is allowed and clicking on the checkbox cycles thru all * By default, the third state is allowed and clicking on the checkbox cycles through all
* three states. If you want that the user can cycle only thru two states, disallow * three states. If you want that the user can cycle only through two states, disallow
* intermediate state using {@link #setAllowIndeterminate(boolean)}. Then you can still * intermediate state using {@link #setAllowIndeterminate(boolean)}. Then you can still
* set the indeterminate state via API if necessary, but the user can not. * set the indeterminate state via API if necessary, but the user can not.
* <p> * <p>

View File

@@ -18,8 +18,8 @@ package com.formdev.flatlaf.extras.resources;
/** /**
* The only purpose of this file is to add a .class file to this package to make it non-empty. * The only purpose of this file is to add a .class file to this package to make it non-empty.
* Otherwise the compiler outputs a warning because this package is opend in module-info.java. * Otherwise, the compiler outputs a warning because this package is opened in module-info.java.
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages. * Also, when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
* *
* @author Karl Tauber * @author Karl Tauber
*/ */

View File

@@ -175,6 +175,9 @@ Spinner.buttonPressedArrowColor = Spinner.buttonArrowColor
#---- SplitPaneDivider ---- #---- SplitPaneDivider ----
SplitPaneDivider.draggingColor = SplitPane.background
SplitPaneDivider.hoverColor = SplitPane.background
SplitPaneDivider.pressedColor = SplitPane.background
SplitPaneDivider.oneTouchHoverArrowColor = SplitPaneDivider.oneTouchArrowColor SplitPaneDivider.oneTouchHoverArrowColor = SplitPaneDivider.oneTouchArrowColor
SplitPaneDivider.oneTouchPressedArrowColor = SplitPaneDivider.oneTouchArrowColor SplitPaneDivider.oneTouchPressedArrowColor = SplitPaneDivider.oneTouchArrowColor

View File

@@ -100,6 +100,6 @@ build script:
artifactId: flatlaf-fonts-inter artifactId: flatlaf-fonts-inter
version: (see button below) version: (see button below)
Otherwise download `flatlaf-fonts-inter-<version>.jar` here: Otherwise, download `flatlaf-fonts-inter-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-inter/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-inter) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-inter/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-inter)

View File

@@ -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"

View File

@@ -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.

View File

@@ -1,6 +1,4 @@
Copyright (c) 2016-2020 The Inter Project Authors. Copyright (c) 2016 The Inter Project Authors (https://github.com/rsms/inter)
"Inter" is trademark of Rasmus Andersson.
https://github.com/rsms/inter
This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: This license is copied below, and is also available with a FAQ at:

View File

@@ -81,6 +81,6 @@ build script:
artifactId: flatlaf-fonts-jetbrains-mono artifactId: flatlaf-fonts-jetbrains-mono
version: (see button below) version: (see button below)
Otherwise download `flatlaf-fonts-jetbrains-mono-<version>.jar` here: Otherwise, download `flatlaf-fonts-jetbrains-mono-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-jetbrains-mono/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-jetbrains-mono) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-jetbrains-mono/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-jetbrains-mono)

View File

@@ -81,6 +81,6 @@ build script:
artifactId: flatlaf-fonts-roboto-mono artifactId: flatlaf-fonts-roboto-mono
version: (see button below) version: (see button below)
Otherwise download `flatlaf-fonts-roboto-mono-<version>.jar` here: Otherwise, download `flatlaf-fonts-roboto-mono-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-roboto-mono/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-roboto-mono) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-roboto-mono/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-roboto-mono)

Some files were not shown because too many files have changed in this diff Show More