Compare commits

...

58 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
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 5860 additions and 2265 deletions

View File

@@ -19,7 +19,7 @@ jobs:
# test against
# - Java 8 (minimum requirement)
# - Java LTS versions (11, 17, ...)
# - lastest Java version(s)
# - latest Java version(s)
java:
- 8
- 11 # LTS
@@ -30,13 +30,13 @@ jobs:
toolchain: 21 # latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1
if: matrix.java == '8'
- name: Setup Java ${{ matrix.java }}
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: temurin # Java 8, 11 and 17 are pre-installed on ubuntu-latest
@@ -50,7 +50,7 @@ jobs:
run: ./gradlew build -Dtoolchain=${{ matrix.toolchain }}
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: matrix.java == '11'
with:
name: FlatLaf-build-artifacts
@@ -70,10 +70,10 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Java 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 11
distribution: temurin # pre-installed on ubuntu-latest
@@ -106,10 +106,10 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Java 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 11
distribution: temurin # pre-installed on ubuntu-latest

View File

@@ -30,10 +30,10 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Java 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 11
distribution: temurin # pre-installed on ubuntu-latest

View File

@@ -20,17 +20,18 @@ jobs:
matrix:
os:
- windows
- macos
- ubuntu
runs-on: ${{ matrix.os }}-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1
- name: Setup Java 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 11
distribution: temurin
@@ -42,7 +43,7 @@ jobs:
run: ./gradlew build-natives --no-daemon
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
path: |

View File

@@ -1,12 +1,95 @@
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 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)
- 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
@@ -170,7 +253,6 @@ FlatLaf Change Log
- Windows DLLs are now digitally signed with FormDev Software GmbH
certificate.
#### Fixed bugs
- FlatLaf window decorations:

View File

@@ -62,7 +62,7 @@ build script:
artifactId: flatlaf
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)
@@ -141,7 +141,6 @@ details and downloads.
Buzz
----
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
- [FlatLaf 3.1 (and 3.0) announcement on Reddit](https://www.reddit.com/r/java/comments/12xgrsu/flatlaf_31_and_30_swing_look_and_feel/)
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)

View File

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

View File

@@ -127,9 +127,11 @@ flatlafPublish {
val natives = "src/main/resources/com/formdev/flatlaf/natives"
nativeArtifacts = listOf(
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),
NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ),
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),
NativeArtifact( "${natives}/libflatlaf-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
#Version 3.2.4
#Version 3.3
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
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_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_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_SQUARE = "JProgressBar.square"
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_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_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_CARD = "card"
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 unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
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
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 UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange"
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_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"
fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont"

View File

@@ -33,7 +33,7 @@ public interface FlatClientProperties
//---- JButton ------------------------------------------------------------
/**
* Specifies type of a button.
* Specifies type of button.
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
@@ -278,12 +278,13 @@ public interface FlatClientProperties
* <p>
* Note that this is not available on all platforms since it requires special support.
* Supported platforms:
* <p>
* <strong>Windows 11</strong> (x86 or x86_64): Only two corner radiuses are supported
* by the OS: {@code DWMWCP_ROUND} is 8px and {@code DWMWCP_ROUNDSMALL} is 4px.
* If this value is {@code 1 - 4}, then {@code DWMWCP_ROUNDSMALL} is used.
* If it is {@code >= 5}, then {@code DWMWCP_ROUND} is used.
* <p>
* <ul>
* <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.
* If this value is {@code 1 - 4}, then {@code DWMWCP_ROUNDSMALL} is used.
* If it is {@code >= 5}, then {@code DWMWCP_ROUND} is used.
* <li><strong>macOS</strong> (10.14 and later): Any corner radius is supported.
* </ul>
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br>
*
@@ -291,6 +292,24 @@ public interface FlatClientProperties
*/
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
* 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";
/**
* 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}.
* <p>
* Setting this shows/hides the "iconfify" button
* Setting this shows/hides the "iconify" button
* for the {@code JFrame} that contains the root pane.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
@@ -487,7 +506,7 @@ public interface FlatClientProperties
* On macOS, Java supports this out of the box.
* <p>
* Note that this client property must be set before the window becomes displayable.
* Otherwise an {@link IllegalComponentStateException} is thrown.
* Otherwise, an {@link IllegalComponentStateException} is thrown.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<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";
/**
* 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.
* <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[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" );
if( bindings != null && rtlBindings != null ) {

View File

@@ -30,9 +30,6 @@ import java.awt.image.ImageProducer;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
@@ -78,6 +75,7 @@ import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
import com.formdev.flatlaf.ui.FlatPopupFactory;
import com.formdev.flatlaf.ui.FlatRootPaneUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.ui.JavaCompatibility2;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.FontUtils;
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.
* <p>
* 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>
* 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.
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
@@ -1295,8 +1287,8 @@ public abstract class FlatLaf
* @since 2.5
*/
public static Map<String, Class<?>> getStyleableInfos( JComponent c ) {
StyleableUI ui = getStyleableUI( c );
return (ui != null) ? ui.getStyleableInfos( c ) : null;
ComponentUI ui = JavaCompatibility2.getUI( c );
return (ui instanceof StyleableUI) ? ((StyleableUI)ui).getStyleableInfos( c ) : null;
}
/**
@@ -1308,41 +1300,10 @@ public abstract class FlatLaf
*/
@SuppressWarnings( "unchecked" )
public static <T> T getStyleableValue( JComponent c, String key ) {
StyleableUI ui = getStyleableUI( c );
return (ui != null) ? (T) ui.getStyleableValue( c, key ) : null;
ComponentUI ui = JavaCompatibility2.getUI( c );
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}.
*

View File

@@ -103,7 +103,10 @@ public interface FlatSystemProperties
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <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";
/**
@@ -169,19 +172,33 @@ public interface FlatSystemProperties
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.
* This can be used to avoid extraction of the native libraries to the temporary directory at runtime.
* <p>
* If the value is {@code "system"}, then {@link System#loadLibrary(String)} is
* used to load the native library.
* Searches for the native library in classloader of caller
* If the value is {@code "system"} (supported since FlatLaf 2.6),
* then {@link System#loadLibrary(String)} is used to load the native library.
* This searches for the native library in classloader of caller
* (using {@link ClassLoader#findLibrary(String)}) and in paths specified
* in system properties {@code sun.boot.library.path} and {@code java.library.path}.
* (supported since FlatLaf 2.6)
* <p>
* If the native library can not loaded from the given path (or via {@link System#loadLibrary(String)}),
* 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.
* <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
*/

View File

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

View File

@@ -172,7 +172,7 @@ debug*/
targetTopY = popupLocation.y;
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 )
subMenuEventQueue = new SubMenuEventQueue();

View File

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

View File

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

View File

@@ -71,7 +71,7 @@ public abstract class FlatWindowAbstractIcon
protected void paintBackground( Component c, Graphics2D g ) {
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
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 );
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.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JViewport;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicBorders;
import com.formdev.flatlaf.FlatClientProperties;
@@ -195,8 +194,7 @@ public class FlatBorder
protected boolean isEnabled( Component c ) {
if( c instanceof JScrollPane ) {
// check whether view component is disabled
JViewport viewport = ((JScrollPane)c).getViewport();
Component view = (viewport != null) ? viewport.getView() : null;
Component view = FlatScrollPaneUI.getView( (JScrollPane) c );
if( view != null && !isEnabled( view ) )
return false;
}

View File

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

View File

@@ -926,7 +926,7 @@ public class FlatComboBoxUI
protected void 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 );
// set popup border
@@ -944,7 +944,7 @@ public class FlatComboBoxUI
if( popupBackground != null )
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
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) );
@@ -1090,7 +1090,7 @@ public class FlatComboBoxUI
}
// using synchronized to avoid problems with code that modifies combo box
// (model, selection, etc) not on AWT thread (which should be not done)
// (model, selection, etc.) not on AWT thread (which should be not done)
synchronized void install( Component c, int focusWidth ) {
if( !(c instanceof JComponent) )
return;
@@ -1242,7 +1242,7 @@ public class FlatComboBoxUI
* Key selection manager that delegates to the default manager.
* Shows the popup if Space key is pressed and "typed characters" buffer is empty.
* If items contain spaces (e.g. "a b") it is still possible to select them
* by pressing keys a, Space and b.
* by pressing keys 'a', 'Space' and 'b'.
*/
private class FlatKeySelectionManager
implements JComboBox.KeySelectionManager, UIResource

View File

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

View File

@@ -22,6 +22,7 @@ import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import javax.swing.JComponent;
/**
* Line border for various components.
@@ -66,6 +67,9 @@ public class FlatLineBorder
@Override
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();
try {
FlatUIUtils.setRenderingHints( g2 );

View File

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

View File

@@ -17,6 +17,7 @@
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
@@ -271,7 +272,7 @@ public class FlatMenuUI
if( !isHover() )
selectionBackground = getStyleFromMenuBarUI( ui -> ui.selectionBackground, menuBarSelectionBackground, selectionBackground );
JMenuBar menuBar = (JMenuBar) menuItem.getParent();
Container menuBar = menuItem.getParent();
JRootPane rootPane = SwingUtilities.getRootPane( menuBar );
if( rootPane != null && rootPane.getParent() instanceof Window &&
rootPane.getJMenuBar() == menuBar &&
@@ -321,12 +322,17 @@ public class FlatMenuUI
}
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) {
MenuBarUI ui = ((JMenuBar)menuItem.getParent()).getUI();
if( !(ui instanceof FlatMenuBarUI) )
return defaultValue;
T value = f.apply( (FlatMenuBarUI) ui );
return (value != null) ? value : defaultValue;
Container menuItemParent = menuItem.getParent();
if( menuItemParent instanceof JMenuBar ) {
MenuBarUI ui = ((JMenuBar) menuItemParent).getUI();
if( ui instanceof FlatMenuBarUI ) {
T value = f.apply( (FlatMenuBarUI) ui );
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,
// 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.
} 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 ) {
// Linux: requires x86_64
@@ -111,13 +117,32 @@ class FlatNativeLibrary
if( library.isLoaded() )
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 {
// try standard library naming scheme
// (same as in flatlaf.jar in package 'com/formdev/flatlaf/natives')
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
if( libraryFile.exists() )
return new NativeLibrary( libraryFile, true );
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
// 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
*/
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 {
// get location of FlatLaf jar
CodeSource codeSource = FlatNativeLibrary.class.getProtectionDomain().getCodeSource();
@@ -162,31 +214,19 @@ class FlatNativeLibrary
if( !jarFile.isFile() )
return null;
// build library file
String jarName = jarFile.getName();
String jarBasename = jarName.substring( 0, jarName.lastIndexOf( '.' ) );
File parent = jarFile.getParentFile();
String libraryName = jarBasename
+ (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf")
+ '-' + classifier + '.' + ext;
// check whether native library exists in same directory as jar
File libraryFile = new File( parent, libraryName );
if( libraryFile.isFile() )
return libraryFile;
// if jar is in "lib" directory, then also check whether library exists
// in "../bin" directory
if( parent.getName().equalsIgnoreCase( "lib" ) ) {
libraryFile = new File( parent.getParentFile(), "bin/" + libraryName );
if( libraryFile.isFile() )
return libraryFile;
}
return jarFile;
} catch( Exception 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() {

View File

@@ -35,6 +35,12 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
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() {
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;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.BorderUIResource;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.JBRCustomDecorations.JBRWindowTopBorder;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
@@ -54,27 +60,15 @@ public class FlatNativeWindowBorder
!SystemInfo.isWinPE &&
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 Provider nativeProvider;
public static boolean isSupported() {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.isSupported();
initialize();
return supported;
}
static Object install( JRootPane rootPane ) {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.install( rootPane );
if( !isSupported() )
return null;
@@ -163,11 +157,6 @@ public class FlatNativeWindowBorder
}
static void uninstall( JRootPane rootPane, Object data ) {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.uninstall( rootPane, data );
return;
}
if( !isSupported() )
return;
@@ -215,9 +204,6 @@ public class FlatNativeWindowBorder
}
public static boolean hasCustomDecoration( Window window ) {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.hasCustomDecoration( window );
if( !isSupported() )
return false;
@@ -225,11 +211,6 @@ public class FlatNativeWindowBorder
}
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setHasCustomDecoration( window, hasCustomDecoration );
return;
}
if( !isSupported() )
return;
@@ -240,11 +221,6 @@ public class FlatNativeWindowBorder
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
{
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
return;
}
if( !isSupported() )
return;
@@ -253,7 +229,7 @@ public class FlatNativeWindowBorder
}
static boolean showWindow( Window window, int cmd ) {
if( canUseJBRCustomDecorations || !isSupported() )
if( !isSupported() )
return false;
return nativeProvider.showWindow( window, cmd );
@@ -320,20 +296,36 @@ public class FlatNativeWindowBorder
* No longer needed since Windows 11.
*/
static class WindowTopBorder
extends JBRCustomDecorations.JBRWindowTopBorder
extends BorderUIResource.EmptyBorderUIResource
{
private static WindowTopBorder instance;
static JBRWindowTopBorder getInstance() {
if( canUseJBRCustomDecorations )
return JBRWindowTopBorder.getInstance();
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 WindowTopBorder getInstance() {
if( instance == null )
instance = new WindowTopBorder();
return instance;
}
@Override
WindowTopBorder() {
super( 1, 0, 0, 0 );
update();
installListeners();
}
void update() {
colorizationAffectsBorders = isColorizationColorAffectsBorders();
activeColor = calculateActiveBorderColor();
}
void installListeners() {
nativeProvider.addChangeListener( e -> {
update();
@@ -346,19 +338,69 @@ public class FlatNativeWindowBorder
} );
}
@Override
boolean isColorizationColorAffectsBorders() {
return nativeProvider.isColorizationColorAffectsBorders();
}
@Override
Color getColorizationColor() {
return nativeProvider.getColorizationColor();
}
@Override
int 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;
import java.awt.Color;
import java.awt.Window;
import com.formdev.flatlaf.util.SystemInfo;
@@ -31,6 +32,12 @@ public class FlatNativeWindowsLibrary
{
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() {
return SystemInfo.isWindows && FlatNativeLibrary.isLoaded();
}
@@ -93,15 +100,60 @@ public class FlatNativeWindowsLibrary
public native static boolean setWindowCornerPreference( long hwnd, int cornerPreference );
/**
* Sets the color of the window border.
* The red/green/blue values must be in range {@code 0 - 255}.
* If red is {@code -1}, then the system default border color is used (useful to reset the border color).
* If red is {@code -2}, then no border is painted.
* <p>
* Invokes Win32 API method {@code DwmSetWindowAttribute(DWMWA_BORDER_COLOR)}.
* DWMWINDOWATTRIBUTE
* see https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
*
* @since 3.3
*/
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
* <p>
* 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.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicOptionPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
@@ -115,13 +113,6 @@ public class FlatOptionPaneUI
sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true );
}
@Override
protected void installComponents() {
super.installComponents();
updateChildPanels( optionPane );
}
@Override
protected PropertyChangeListener createPropertyChangeListener() {
PropertyChangeListener superListener = super.createPropertyChangeListener();
@@ -155,6 +146,13 @@ public class FlatOptionPaneUI
protected Container 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
if( iconMessageGap > 0 ) {
Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" );
@@ -169,6 +167,10 @@ public class FlatOptionPaneUI
protected Container 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
if( buttonArea.getLayout() instanceof ButtonAreaLayout ) {
ButtonAreaLayout layout = (ButtonAreaLayout) buttonArea.getLayout();
@@ -218,22 +220,33 @@ public class FlatOptionPaneUI
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() ) {
if( child.getClass() == JPanel.class ) {
JPanel panel = (JPanel)child;
// make sub-panel non-opaque for OptionPane.background
panel.setOpaque( false );
// use non-UIResource borders to avoid that they are replaced when switching LaF
Border border = panel.getBorder();
if( border instanceof UIResource )
panel.setBorder( FlatUIUtils.nonUIResource( border ) );
if( child instanceof JPanel && child.getName() != null ) {
switch( child.getName() ) {
case "OptionPane.realBody":
case "OptionPane.body":
case "OptionPane.separator":
case "OptionPane.break":
// make known sub-panels non-opaque for OptionPane.background
((JPanel)child).setOpaque( false );
break;
}
}
if( child instanceof Container )
updateChildPanels( (Container) child );
updateKnownChildPanels( (Container) child );
}
}

View File

@@ -71,6 +71,8 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatPopupFactory
extends PopupFactory
{
static final String KEY_POPUP_USES_NATIVE_BORDER = "FlatLaf.internal.FlatPopupFactory.popupUsesNativeBorder";
private MethodHandle java8getPopupMethod;
private MethodHandle java9getPopupMethod;
@@ -92,17 +94,20 @@ public class FlatPopupFactory
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
// macOS and Linux adds drop shadow to heavy weight popups
if( SystemInfo.isMacOS || SystemInfo.isLinux )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
if( SystemInfo.isMacOS || SystemInfo.isLinux ) {
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
int borderCornerRadius;
if( isWindows11BorderSupported() &&
(borderCornerRadius = getBorderCornerRadius( owner, contents )) > 0 )
getBorderCornerRadius( owner, contents ) > 0 )
{
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
if( popup.popupWindow != null )
setupWindows11Border( popup.popupWindow, contents, borderCornerRadius );
setupRoundedBorder( popup.popupWindow, owner, contents );
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,
* 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).
* In case that the tooltip would be partly outside of the screen,
@@ -342,48 +313,58 @@ public class FlatPopupFactory
((JComponent)owner).getToolTipLocation( me ) != null;
}
//---- native rounded border ----------------------------------------------
private static boolean isWindows11BorderSupported() {
return SystemInfo.isWindows_11_orLater && FlatNativeWindowsLibrary.isLoaded();
}
private static void setupWindows11Border( Window popupWindow, Component contents, int borderCornerRadius ) {
// make sure that the Windows 11 window is created
private static void setupRoundedBorder( Window popupWindow, Component owner, Component contents ) {
// make sure that the native window is created
if( !popupWindow.isDisplayable() )
popupWindow.addNotify();
// get window handle
long hwnd = FlatNativeWindowsLibrary.getHWND( popupWindow );
int borderCornerRadius = getBorderCornerRadius( owner, contents );
float borderWidth = getRoundedBorderWidth( owner, contents );
// set corner preference
int cornerPreference = (borderCornerRadius <= 4)
? 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;
// get Swing border color
Color borderColor = null; // use system default color
if( contents instanceof JComponent ) {
Border border = ((JComponent)contents).getBorder();
border = FlatUIUtils.unwrapNonUIResourceBorder( border );
// get color from border of contents (e.g. JPopupMenu or JToolTip)
Color borderColor = null;
if( border instanceof FlatLineBorder )
borderColor = ((FlatLineBorder)border).getLineColor();
else if( border instanceof LineBorder )
borderColor = ((LineBorder)border).getLineColor();
else if( border instanceof EmptyBorder )
red = -2; // do not paint border
borderColor = FlatNativeWindowsLibrary.COLOR_NONE; // do not paint border
if( borderColor != null ) {
red = borderColor.getRed();
green = borderColor.getGreen();
blue = borderColor.getBlue();
}
// avoid that FlatLineBorder paints the Swing border
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, true );
}
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 ) {
@@ -396,7 +377,34 @@ public class FlatPopupFactory
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 ) {
if( owner == null )
return false;
Window window = SwingUtilities.getWindowAncestor( owner );
if( window == null )
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,
* which hides the popup as soon as the owner/invoker window looses focus.
* 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
* are broken since Java 21.
*
@@ -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 ---------------------------------------------
private static class NonFlashingPopup
@@ -508,6 +541,9 @@ public class FlatPopupFactory
@Override
public void hide() {
if( contents instanceof JComponent )
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, null );
if( delegate != null ) {
delegate.hide();
delegate = null;

View File

@@ -263,7 +263,7 @@ public class FlatRadioButtonUI
@Override
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
// - if background color is different to default background color
// (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.basic.BasicRootPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -156,10 +155,6 @@ public class FlatRootPaneUI
if( background == null || background instanceof UIResource )
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
@@ -499,7 +494,7 @@ public class FlatRootPaneUI
@Override
public void invalidateLayout( Container parent ) {
if( titlePane != null )
titlePane.menuBarChanged();
titlePane.menuBarInvalidate();
}
@Override

View File

@@ -245,7 +245,7 @@ public class FlatScrollBarUI
// because scroll bars do not receive mouse exited event.
// The scroll pane, including its scroll bars, is not part
// 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
// are sent only for the whole 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;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
@@ -41,16 +44,19 @@ import javax.swing.JTree;
import javax.swing.JViewport;
import javax.swing.LookAndFeel;
import javax.swing.ScrollPaneConstants;
import javax.swing.ScrollPaneLayout;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
@@ -97,7 +103,13 @@ public class FlatScrollPaneUI
super.installUI( c );
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();
@@ -108,6 +120,10 @@ public class FlatScrollPaneUI
public void uninstallUI( JComponent c ) {
MigLayoutVisualPadding.uninstall( scrollpane );
// uninstall layout manager
if( c.getLayout() instanceof FlatScrollPaneLayout )
c.setLayout( new ScrollPaneLayout.UIResource() );
super.uninstallUI( c );
oldStyleValues = null;
@@ -130,6 +146,13 @@ public class FlatScrollPaneUI
handler = null;
}
/**
* @since 3.3
*/
protected FlatScrollPaneLayout createScrollPaneLayout() {
return new FlatScrollPaneLayout();
}
@Override
protected MouseWheelListener createMouseWheelListener() {
MouseWheelListener superListener = super.createMouseWheelListener();
@@ -290,8 +313,7 @@ public class FlatScrollPaneUI
Object corner = e.getNewValue();
if( corner instanceof JButton &&
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
scrollpane.getViewport() != null &&
scrollpane.getViewport().getView() instanceof JTable )
getView( scrollpane ) instanceof JTable )
{
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
((JButton)corner).setFocusable( false );
@@ -308,6 +330,18 @@ public class FlatScrollPaneUI
scrollpane.revalidate();
scrollpane.repaint();
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 */
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" );
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 )
@@ -402,13 +437,46 @@ public class FlatScrollPaneUI
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 );
}
@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 */
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
JViewport viewport = scrollPane.getViewport();
Component view = (viewport != null) ? viewport.getView() : null;
Component view = getView( scrollPane );
if( view == null )
return false;
@@ -428,6 +496,25 @@ public class FlatScrollPaneUI
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 ------------------------------------------------------
/**
@@ -450,13 +537,71 @@ public class FlatScrollPaneUI
@Override
public void focusGained( FocusEvent e ) {
// necessary to update focus border
scrollpane.repaint();
if( scrollpane.getBorder() instanceof FlatBorder )
scrollpane.repaint();
}
@Override
public void focusLost( FocusEvent e ) {
// 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;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Graphics;
@@ -67,6 +69,8 @@ import com.formdev.flatlaf.util.UIScale;
* <!-- FlatSplitPaneUI -->
*
* @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault SplitPaneDivider.hoverColor Color optional
* @uiDefault SplitPaneDivider.pressedColor Color optional
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
* @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color
@@ -83,6 +87,7 @@ public class FlatSplitPaneUI
implements StyleableUI
{
@Styleable protected String arrowType;
/** @since 3.3 */ @Styleable protected Color draggingColor;
@Styleable protected Color oneTouchArrowColor;
@Styleable protected Color oneTouchHoverArrowColor;
@Styleable protected Color oneTouchPressedArrowColor;
@@ -104,6 +109,8 @@ public class FlatSplitPaneUI
protected void installDefaults() {
arrowType = UIManager.getString( "Component.arrowType" );
draggingColor = UIManager.getColor( "SplitPaneDivider.draggingColor" );
// get one-touch colors before invoking super.installDefaults() because they are
// used in there on LaF switching
oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" );
@@ -117,6 +124,8 @@ public class FlatSplitPaneUI
protected void uninstallDefaults() {
super.uninstallDefaults();
draggingColor = null;
oneTouchArrowColor = null;
oneTouchHoverArrowColor = null;
oneTouchPressedArrowColor = null;
@@ -183,12 +192,49 @@ public class FlatSplitPaneUI
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 -----------------------------------------
protected class FlatSplitPaneDivider
extends BasicSplitPaneDivider
{
@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 int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
@Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
@@ -251,15 +297,31 @@ public class FlatSplitPaneUI
@Override
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 );
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 ) )
return;
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
g.setColor( gripColor );
paintGrip( g, 0, 0, getWidth(), getHeight() );
paintGrip( g, x, y, width, height );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
@@ -286,6 +348,29 @@ public class FlatSplitPaneUI
: 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 ---------------------------------------
protected class FlatOneTouchButton

View File

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

View File

@@ -17,6 +17,7 @@
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
@@ -26,18 +27,25 @@ import java.awt.Insets;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Graphics2DProxy;
@@ -116,6 +124,7 @@ public class FlatTableUI
private boolean oldShowHorizontalLines;
private boolean oldShowVerticalLines;
private Dimension oldIntercellSpacing;
private TableCellRenderer oldBooleanRenderer;
private PropertyChangeListener propertyChangeListener;
private Map<String, Object> oldStyleValues;
@@ -151,19 +160,35 @@ public class FlatTableUI
if( rowHeight > 0 )
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();
table.setShowHorizontalLines( false );
}
if( !showVerticalLines ) {
if( !showVerticalLines && (watcher == null || !watcher.showVerticalLinesChanged) ) {
oldShowVerticalLines = table.getShowVerticalLines();
table.setShowVerticalLines( false );
}
if( intercellSpacing != null ) {
if( intercellSpacing != null && (watcher == null || !watcher.intercellSpacingChanged) ) {
oldIntercellSpacing = table.getIntercellSpacing();
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
@@ -177,15 +202,36 @@ public class FlatTableUI
oldStyleValues = null;
FlatTablePropertyWatcher watcher = FlatTablePropertyWatcher.get( table );
if( watcher != null )
watcher.enabled = false;
// restore old show horizontal/vertical lines (if not modified)
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() )
table.setShowHorizontalLines( true );
if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() )
table.setShowVerticalLines( true );
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() &&
(watcher == null || !watcher.showHorizontalLinesChanged) )
table.setShowHorizontalLines( true );
if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() &&
(watcher == null || !watcher.showVerticalLinesChanged) )
table.setShowVerticalLines( true );
// restore old intercell spacing (if not modified)
if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) )
table.setIntercellSpacing( oldIntercellSpacing );
if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) &&
(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
@@ -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();
}
void menuBarInvalidate() {
menuBarPlaceholder.invalidate();
}
@Override
public void paint( Graphics g ) {
super.paint( g );
@@ -835,10 +839,6 @@ public class FlatTitlePane
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.
*/
@@ -896,10 +896,7 @@ public class FlatTitlePane
iconBounds.width += iconInsets.right;
}
if( hasJBRCustomDecoration() )
hitTestSpots.add( iconBounds );
else
appIconBounds = iconBounds;
appIconBounds = iconBounds;
} else if( showIconBesideTitle && titleLabel.getIcon() != null && titleLabel.getUI() instanceof FlatTitleLabelUI ) {
FlatTitleLabelUI ui = (FlatTitleLabelUI) titleLabel.getUI();
@@ -927,10 +924,7 @@ public class FlatTitlePane
iconR.width += 2;
iconR.height += 2;
if( hasJBRCustomDecoration() )
hitTestSpots.add( iconR );
else
appIconBounds = iconR;
appIconBounds = iconR;
}
}
@@ -1270,7 +1264,7 @@ debug*/
public void mouseClicked( MouseEvent e ) {
// on Linux, when using native library, the mouse clicked 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 ) ) {
// see comment in mousePressed()
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 java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
@@ -173,6 +174,12 @@ public class FlatToolBarSeparatorUI
if( size != null )
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
int sepWidth = (scale( (separatorWidth - LINE_WIDTH) / 2 ) * 2) + scale( LINE_WIDTH );
@@ -196,6 +203,12 @@ public class FlatToolBarSeparatorUI
float lineWidth = scale( 1f );
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 );
g.setColor( separatorColor );
@@ -210,4 +223,11 @@ public class FlatToolBarSeparatorUI
private boolean isVertical( JComponent c ) {
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 Color gripColor;
// for FlatToolBarSeparatorUI
/** @since 3.3 */ @Styleable protected Integer separatorWidth;
/** @since 3.3 */ @Styleable protected Color separatorColor;
private FocusTraversalPolicy focusTraversalPolicy;
private Boolean oldFloatable;
private Map<String, Object> oldStyleValues;

View File

@@ -299,15 +299,10 @@ public class FlatUIUtils
if( c == null )
return false;
// check whether used in cell editor (check 3 levels up)
Component c2 = c;
for( int i = 0; i <= 2 && c2 != null; i++ ) {
Container parent = c2.getParent();
if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c2 )
return true;
c2 = parent;
}
// check whether used as table cell editor
Container parent = c.getParent();
if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c )
return true;
// check whether used as cell editor
// 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).
*/
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.Graphics;
import java.lang.reflect.Method;
import javax.swing.JComponent;
import javax.swing.JViewport;
import javax.swing.plaf.ComponentUI;
@@ -48,14 +47,9 @@ public class FlatViewportUI
Component view = ((JViewport)c).getView();
if( view instanceof JComponent ) {
try {
Method m = view.getClass().getMethod( "getUI" );
Object ui = m.invoke( view );
if( ui instanceof ViewportPainter )
((ViewportPainter)ui).paintViewport( g, (JComponent) view, (JViewport) c );
} catch( Exception ex ) {
// ignore
}
ComponentUI ui = JavaCompatibility2.getUI( (JComponent) view );
if( ui instanceof ViewportPainter )
((ViewportPainter)ui).paintViewport( g, (JComponent) view, (JViewport) c );
}
}

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)}.
* 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 ) {
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
* appends families registered for lazy loading via {@link #registerFontFamilyLoader(String, Runnable)}
* to the result.

View File

@@ -192,7 +192,8 @@ public class HiDPIUtils
case "Inter":
case "Inter Light":
case "Inter Semi Bold":
case "Inter Semi Bold": // Inter v3
case "Inter SemiBold": // Inter v4
case "Roboto":
case "Roboto Light":
case "Roboto Medium":

View File

@@ -116,7 +116,11 @@ public class NativeLibrary
try {
// for development environment
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() ) {
// load library without copying
System.load( libraryFile.getCanonicalPath() );

View File

@@ -204,7 +204,7 @@ public class UIScale
if( SystemInfo.isWindows ) {
// Special handling for Windows to be compatible with OS 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.
// - Windows "text scaling" increases only the font size, but on all screens.
//

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -28,6 +28,8 @@ import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import com.formdev.flatlaf.*;
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.SystemInfo;
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 IntelliJ (F3)", FlatIntelliJLaf.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();
for( UIManager.LookAndFeelInfo lookAndFeel : lookAndFeels ) {
@@ -127,6 +131,8 @@ class ControlBar
registerSwitchToLookAndFeel( KeyEvent.VK_F2, FlatDarkLaf.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F3, FlatIntelliJLaf.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 )
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.Dimension;
import java.awt.EventQueue;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
@@ -116,15 +115,16 @@ class DataComponentsPanel
table1.setGridColor( redGridColorCheckBox.isSelected() ? Color.red : UIManager.getColor( "Table.gridColor" ) );
}
@Override
public void updateUI() {
super.updateUI();
private void showHorizontalLinesPropertyChange() {
showHorizontalLinesCheckBox.setSelected( table1.getShowHorizontalLines() );
}
EventQueue.invokeLater( () -> {
showHorizontalLinesChanged();
showVerticalLinesChanged();
intercellSpacingChanged();
} );
private void showVerticalLinesPropertyChange() {
showVerticalLinesCheckBox.setSelected( table1.getShowVerticalLines() );
}
private void intercellSpacingPropertyChange() {
intercellSpacingCheckBox.setSelected( table1.getRowMargin() != 0 );
}
@SuppressWarnings( { "unchecked", "rawtypes" } )
@@ -333,10 +333,10 @@ class DataComponentsPanel
"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
};
boolean[] columnEditable = new boolean[] {
boolean[] columnEditable = {
false, true, true, true, true, true
};
@Override
@@ -383,6 +383,9 @@ class DataComponentsPanel
}
table1.setAutoCreateRowSorter(true);
table1.setComponentPopupMenu(popupMenu2);
table1.addPropertyChangeListener("showHorizontalLines", e -> showHorizontalLinesPropertyChange());
table1.addPropertyChangeListener("showVerticalLines", e -> showVerticalLinesPropertyChange());
table1.addPropertyChangeListener("rowMargin", e -> intercellSpacingPropertyChange());
scrollPane5.setViewportView(table1);
}
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 {
contentType: "form/swing"
@@ -333,6 +333,9 @@ new FormModel {
auxiliary() {
"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 ) {
"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.FlatMacLightLaf;
import com.formdev.flatlaf.extras.FlatSVGUtils;
import com.formdev.flatlaf.ui.JBRCustomDecorations;
import com.formdev.flatlaf.util.ColorFunctions;
import com.formdev.flatlaf.util.FontUtils;
import com.formdev.flatlaf.util.LoggingFacade;
@@ -931,12 +930,6 @@ class DemoFrame
menuBarEmbeddedCheckBoxMenuItem.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) );
unifiedTitleBarMenuItem.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) );
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 {
unsupported( windowDecorationsCheckBoxMenuItem );
unsupported( menuBarEmbeddedCheckBoxMenuItem );

View File

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

View File

@@ -204,7 +204,7 @@ class TabsPanel
private void closeButtonStyleChanged() {
// WARNING:
// 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() ) {
UIManager.put( "TabbedPane.closeArc", 999 );
UIManager.put( "TabbedPane.closeCrossFilledSize", 5.5f );
@@ -313,6 +313,14 @@ class TabsPanel
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 ) {
updateTabbedPanesRecur( this, tabbedPane -> tabbedPane.putClientProperty( key, value ) );
}
@@ -331,6 +339,8 @@ class TabsPanel
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JScrollPane tabsScrollPane = new JScrollPane();
ScrollablePanel panel6 = new ScrollablePanel();
JPanel panel1 = new JPanel();
JLabel tabPlacementLabel = new JLabel();
tabPlacementToolBar = new JToolBar();
@@ -397,344 +407,369 @@ class TabsPanel
scrollAsNeededSingleButton = new JToggleButton();
scrollAsNeededButton = new JToggleButton();
scrollNeverButton = new JToggleButton();
scrollButtonsPlacementLabel = new JLabel();
scrollButtonsPlacementToolBar = new JToolBar();
scrollBothButton = new JToggleButton();
scrollTrailingButton = new JToggleButton();
showTabSeparatorsCheckBox = new JCheckBox();
tabsPopupPolicyLabel = new JLabel();
tabsPopupPolicyToolBar = new JToolBar();
popupAsNeededButton = new JToggleButton();
popupNeverButton = new JToggleButton();
showTabSeparatorsCheckBox = new JCheckBox();
scrollButtonsPlacementLabel = new JLabel();
scrollButtonsPlacementToolBar = new JToolBar();
scrollBothButton = new JToggleButton();
scrollTrailingButton = new JToggleButton();
tabTypeLabel = new JLabel();
tabTypeToolBar = new JToolBar();
underlinedTabTypeButton = new JToggleButton();
cardTabTypeButton = new JToggleButton();
tabRotationLabel = new JLabel();
tabRotationToolBar = new JToolBar();
rotationNoneButton = new JToggleButton();
rotationAutoButton = new JToggleButton();
rotationLeftButton = new JToggleButton();
rotationRightButton = new JToggleButton();
//======== this ========
setLayout(new MigLayout(
"insets dialog,hidemode 3",
"insets 0,hidemode 3",
// columns
"[grow,fill]para" +
"[fill]para" +
"[fill]",
"[grow,fill]",
// rows
"[grow,fill]para" +
"[]" +
"[grow,fill]0" +
"[]0" +
"[]"));
//======== panel1 ========
//======== tabsScrollPane ========
{
panel1.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]" +
"[fill]para" +
"[]0" +
"[]" +
"[]para" +
"[]" +
"[]para" +
"[]" +
"[]"));
tabsScrollPane.setBorder(BorderFactory.createEmptyBorder());
//---- tabPlacementLabel ----
tabPlacementLabel.setText("Tab placement");
tabPlacementLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabPlacementLabel, "cell 0 0");
//======== tabPlacementToolBar ========
//======== panel6 ========
{
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");
}
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",
panel6.setLayout(new MigLayout(
"insets dialog,hidemode 3",
// columns
"[grow,fill]para" +
"[fill]para" +
"[fill]",
// rows
"[]" +
"[]" +
"[]" +
"[]"));
"[grow,fill]"));
//---- 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 ========
//======== panel1 ========
{
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");
panel5.add(tabAlignCenterTabbedPane, "cell 0 2");
panel5.add(tabAlignTrailingTabbedPane, "cell 0 3");
panel6.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");
}
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(separator2, "cell 0 1 3 1");
add(tabsScrollPane, "cell 0 0");
add(separator2, "cell 0 1");
//======== panel4 ========
{
panel4.setLayout(new MigLayout(
"insets 0,hidemode 3",
"insets panel,hidemode 3",
// columns
"[]" +
"[fill]para" +
"[fill]" +
"[fill]para" +
"[fill]" +
"[fill]",
// rows
"[]" +
@@ -770,38 +805,9 @@ class TabsPanel
}
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.setText("Tabs popup policy:");
panel4.add(tabsPopupPolicyLabel, "cell 0 1");
panel4.add(tabsPopupPolicyLabel, "cell 2 0");
//======== tabsPopupPolicyToolBar ========
{
@@ -821,7 +827,36 @@ class TabsPanel
popupNeverButton.addActionListener(e -> tabsPopupPolicyChanged());
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.setText("Tab type:");
@@ -845,8 +880,44 @@ class TabsPanel
tabTypeToolBar.add(cardTabTypeButton);
}
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 ----
ButtonGroup tabPlacementButtonGroup = new ButtonGroup();
@@ -872,20 +943,27 @@ class TabsPanel
scrollButtonsPolicyButtonGroup.add(scrollAsNeededButton);
scrollButtonsPolicyButtonGroup.add(scrollNeverButton);
//---- scrollButtonsPlacementButtonGroup ----
ButtonGroup scrollButtonsPlacementButtonGroup = new ButtonGroup();
scrollButtonsPlacementButtonGroup.add(scrollBothButton);
scrollButtonsPlacementButtonGroup.add(scrollTrailingButton);
//---- tabsPopupPolicyButtonGroup ----
ButtonGroup tabsPopupPolicyButtonGroup = new ButtonGroup();
tabsPopupPolicyButtonGroup.add(popupAsNeededButton);
tabsPopupPolicyButtonGroup.add(popupNeverButton);
//---- scrollButtonsPlacementButtonGroup ----
ButtonGroup scrollButtonsPlacementButtonGroup = new ButtonGroup();
scrollButtonsPlacementButtonGroup.add(scrollBothButton);
scrollButtonsPlacementButtonGroup.add(scrollTrailingButton);
//---- tabTypeButtonGroup ----
ButtonGroup tabTypeButtonGroup = new ButtonGroup();
tabTypeButtonGroup.add(underlinedTabTypeButton);
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
if( FlatLafDemo.screenshotsMode ) {
@@ -961,18 +1039,24 @@ class TabsPanel
private JToggleButton scrollAsNeededSingleButton;
private JToggleButton scrollAsNeededButton;
private JToggleButton scrollNeverButton;
private JLabel scrollButtonsPlacementLabel;
private JToolBar scrollButtonsPlacementToolBar;
private JToggleButton scrollBothButton;
private JToggleButton scrollTrailingButton;
private JCheckBox showTabSeparatorsCheckBox;
private JLabel tabsPopupPolicyLabel;
private JToolBar tabsPopupPolicyToolBar;
private JToggleButton popupAsNeededButton;
private JToggleButton popupNeverButton;
private JCheckBox showTabSeparatorsCheckBox;
private JLabel scrollButtonsPlacementLabel;
private JToolBar scrollButtonsPlacementToolBar;
private JToggleButton scrollBothButton;
private JToggleButton scrollTrailingButton;
private JLabel tabTypeLabel;
private JToolBar tabTypeToolBar;
private JToggleButton underlinedTabTypeButton;
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
}

View File

@@ -33,7 +33,7 @@ build script:
artifactId: flatlaf-extras
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)

View File

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

View File

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

View File

@@ -87,7 +87,7 @@ public class FlatSVGIcon
* in the tag {@code <svg>} are used as icon size.
* <p>
* 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>
* 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.
* <p>
* 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>
* 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.
* <p>
* 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>
* 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.
* <p>
* 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>
* This is cheap operation because the icon is only loaded when used.
*
@@ -166,7 +166,7 @@ public class FlatSVGIcon
* by the given scale factor.
* <p>
* 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>
* This is cheap operation because the icon is only loaded when used.
*
@@ -187,7 +187,7 @@ public class FlatSVGIcon
* by the given scale factor.
* <p>
* 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>
* This is cheap operation because the icon is only loaded when used.
*
@@ -259,7 +259,7 @@ public class FlatSVGIcon
* <p>
* 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
* @since 2
*/

View File

@@ -46,10 +46,10 @@ public class FlatSVGUtils
* then a single multi-resolution image is returned that creates images on demand
* for requested sizes from SVG.
* 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>
* 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)
* @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
* for requested sizes from SVG.
* 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>
* This method is useful if using Java modules and the package containing the SVG
* is not opened in {@code module-info.java}.
@@ -92,7 +92,7 @@ public class FlatSVGUtils
// any size is created on demand when
// MultiResolutionImage.getResolutionVariant(double destImageWidth, double destImageHeight)
// 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( 20, 20 ), // 125%
new Dimension( 24, 24 ), // 150%
@@ -120,7 +120,7 @@ public class FlatSVGUtils
* Creates a buffered image and renders the given SVG into it.
* <p>
* 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 width the width of the image
@@ -154,7 +154,7 @@ public class FlatSVGUtils
* Creates a buffered image and renders the given SVG into it.
* <p>
* 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 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
panel = new JPanel();
filterPanel = new JPanel();
flterLabel = new JLabel();
filterLabel = new JLabel();
filterField = new FlatTextField();
valueTypeLabel = new JLabel();
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()).rowWeights = new double[] {0.0, 1.0E-4};
//---- flterLabel ----
flterLabel.setText("Filter:");
flterLabel.setLabelFor(filterField);
flterLabel.setDisplayedMnemonic('F');
filterPanel.add(flterLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
//---- filterLabel ----
filterLabel.setText("Filter:");
filterLabel.setLabelFor(filterField);
filterLabel.setDisplayedMnemonic('F');
filterPanel.add(filterLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 10), 0, 0));
@@ -668,7 +668,7 @@ public class FlatUIDefaultsInspector
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JPanel panel;
private JPanel filterPanel;
private JLabel flterLabel;
private JLabel filterLabel;
private FlatTextField filterField;
private JLabel valueTypeLabel;
private JComboBox<String> valueTypeField;

View File

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

View File

@@ -33,14 +33,14 @@ public class FlatButton
public enum ButtonType { none, square, roundRect, tab, help, toolBarButton, borderless }
/**
* Returns type of a button.
* Returns type of button.
*/
public ButtonType getButtonType() {
return getClientPropertyEnumString( BUTTON_TYPE, ButtonType.class, null, ButtonType.none );
}
/**
* Specifies type of a button.
* Specifies type of button.
*/
public void setButtonType( ButtonType buttonType ) {
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
* slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}.
* (see {@link #setOutline(Object)}).
*
* @since 2
*/
@@ -135,7 +135,7 @@ public class FlatFormattedTextField
* The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}.
* (see {@link #setOutline(Object)}).
*
* @since 2
*/

View File

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

View File

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

View File

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

View File

@@ -30,8 +30,8 @@ import com.formdev.flatlaf.FlatLaf;
* <p>
* The initial state is {@link State#INDETERMINATE}.
* <p>
* By default the third state is allowed and clicking on the checkbox cycles thru all
* three states. If you want that the user can cycle only thru two states, disallow
* 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 through two states, disallow
* intermediate state using {@link #setAllowIndeterminate(boolean)}. Then you can still
* set the indeterminate state via API if necessary, but the user can not.
* <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.
* Otherwise the compiler outputs a warning because this package is opend in module-info.java.
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
* Otherwise, the compiler outputs a warning because this package is opened in module-info.java.
* Also, when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
*
* @author Karl Tauber
*/

View File

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

View File

@@ -100,6 +100,6 @@ build script:
artifactId: flatlaf-fonts-inter
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)

View File

@@ -18,7 +18,7 @@
// 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
// necessary, but the <font-version> has not changed.
version = "3.19"
version = "4.0"
if( !rootProject.hasProperty( "release" ) )
version = version.toString() + "-SNAPSHOT"

View File

@@ -117,7 +117,7 @@ public class FlatInterFont
* new Font( FlatInterFont.FAMILY_SEMIBOLD, Font.ITALIC, 12 );
* }</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.

View File

@@ -1,6 +1,4 @@
Copyright (c) 2016-2020 The Inter Project Authors.
"Inter" is trademark of Rasmus Andersson.
https://github.com/rsms/inter
Copyright (c) 2016 The Inter Project Authors (https://github.com/rsms/inter)
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:

View File

@@ -81,6 +81,6 @@ build script:
artifactId: flatlaf-fonts-jetbrains-mono
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)

View File

@@ -81,6 +81,6 @@ build script:
artifactId: flatlaf-fonts-roboto-mono
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)

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