Compare commits

...

58 Commits

Author SHA1 Message Date
Karl Tauber
bde25f6ac8 release 3.4.1 2024-03-29 13:28:58 +01:00
Karl Tauber
c989b97ffa fixed custom properties file names for nested classes (e.g. IntelliJTheme$ThemeLaf.properties instead of ThemeLaf.properties)
(see issue #824)
2024-03-29 13:14:29 +01:00
Karl Tauber
5f5c225300 macOS: fixed crash when running in WebSwing (issue #826; regression in 3.4) 2024-03-26 13:24:05 +01:00
Karl Tauber
36e4071b7f FlatSVGIcon: use log level CONFIG instead of SEVERE and allow disabling logging (issue #823) 2024-03-24 17:29:57 +01:00
Karl Tauber
1068884bce change snapshot version from 3.5-SNAPSHOT to 3.4.1-SNAPSHOT 2024-03-24 17:10:43 +01:00
Karl Tauber
32d102dbc9 Native Libraries: added API version to:
- test whether native library matches the JAR (bad builds could e.g. ship a newer JAR with an older incompatible native library)
- invoke a method (to get API version) to check whether native library works correctly

if API version do not match, or method could not invoked correctly, disable usage of FlatLaf native library

Windows and macOS binaries built and signed locally in clean workspace
Linux binary built by GitHub Actions
2024-03-24 16:37:11 +01:00
Karl Tauber
4e1f092b98 FlatClientProperties: javadoc fixes 2024-03-24 12:45:48 +01:00
Karl Tauber
bd60a18ff4 SplitPane:
- update divider when client property `JSplitPane.expandableSide` changed
- Extras: added support for `JSplitPane.expandableSide` client property to `FlatSplitPane`
2024-03-24 12:42:39 +01:00
Karl Tauber
3b3d7d76eb Testing: added tab with random background color to FlatContainerTest and FlatJideOssContainerTest 2024-03-23 16:33:27 +01:00
Karl Tauber
ec76448e9f jsvg: updated to 1.4.0
jide-oss: updated to 3.7.15
2024-03-23 15:31:45 +01:00
Karl Tauber
872c84974c Merge PR #822: Request to add MCreator to list of apps using FlatLAF 2024-03-23 15:08:20 +01:00
Karl Tauber
5dd2008969 GitHub Actions: build using Java 22 (use toolchain because Gradle 8.7 does not support running on Java 22) 2024-03-23 14:13:43 +01:00
Karl Tauber
55ddac2bc7 Gradle: use simpler Kotlin DSL property assignment
https://blog.gradle.org/simpler-kotlin-dsl-property-assignment
2024-03-23 13:56:47 +01:00
Karl Tauber
a62dd22f83 Gradle: fixed warning regarding using deprecation Gradle features, making it incompatible with Gradle 9.0 2024-03-23 13:46:51 +01:00
Karl Tauber
a503879858 update to Gradle 8.7 2024-03-23 13:10:58 +01:00
Klemen
14f19dd735 Update README.md 2024-03-18 09:07:56 +01:00
Karl Tauber
9a727f68ce Window decorations: fixed missing window top border on Windows 10 in "full window content" mode (issue #809) 2024-03-17 18:09:19 +01:00
Karl Tauber
d26819d268 TabbedPane:
- updated comment regarding unsupported scroll buttons in right-to-left component orientation (see issue #815)
- removed unused on wrong code that tries to layout scroll buttons for right-to-left
2024-03-13 00:03:05 +01:00
Karl Tauber
44752cc9aa TabbedPane: fixed swapped back and forward scroll buttons when using TabbedPane.scrollButtonsPlacement = trailing (regression in FlatLaf 3.3 since commit 97495a6093) 2024-03-12 18:41:47 +01:00
Karl Tauber
11e0757387 FlatSVGIcon:
- setColorFilter() now returns `this`
- added method to enable/disable and clear SVGDocument cache
- do not filter red color in `paintSvgError()`
2024-03-12 13:36:56 +01:00
Karl Tauber
1f1ebc3c44 FlatSVGIcon: some additions to PR #817:
- create new `LinearGradientPaint` only if color has changed
- preserve old `LinearGradientPaint.colorSpace` and `transform`
2024-03-12 12:20:31 +01:00
Karl Tauber
4be97b6ea6 Merge PR #817: Add LinearGradient paint in svg color filter 2024-03-11 19:04:47 +01:00
laing raven
3facca5499 add LinearGradient paint in svg color filter 2024-03-10 19:12:29 +07:00
Karl Tauber
bfbd25012a release 3.4 2024-02-19 23:29:00 +01:00
Karl Tauber
063fff2ab4 gradle: fixed dependency of task ':flatlaf-core:signMavenPublication' (type 'Sign') 2024-02-19 23:28:31 +01:00
Karl Tauber
fbdc8d5b99 Merge branch 'windows-full-window-content' 2024-02-19 22:25:04 +01:00
Karl Tauber
625c0a3321 Tree: detect tree cell editor in FlatUIUtils.isCellEditor() 2024-02-19 22:12:29 +01:00
Karl Tauber
2972300112 Table: select all text in cell editor when starting editing using F2 key; can be disabled using Table.editorSelectAllOnStartEditing (issue 652)
also added missing `Table.paintOutsideAlternateRows`
2024-02-19 22:03:19 +01:00
Karl Tauber
a8e71895ee gradle: use AbstractPublishToMaven instead of PublishToMavenRepository to support publishing to local Maven repo (PublishToMavenLocal) 2024-02-19 18:47:48 +01:00
Karl Tauber
d7a76081e3 FileChooser: extended FlatFileChooserTest to support testing various locales (issue #795) 2024-02-12 12:05:01 +01:00
Karl Tauber
fd925a6718 FileChooser: extended FlatFileChooserTest to support testing all JFileChooser properties (issue #795) 2024-02-10 19:35:53 +01:00
Karl Tauber
4fc890a77c Testing: split FlatChooserTest into FlatFileChooserTest and FlatChooserTest 2024-02-10 14:16:45 +01:00
Karl Tauber
b804463b73 Window decorations:
- fixed updating of client property `FULL_WINDOW_CONTENT_BUTTONS_BOUNDS` when resizing window
- fixed title bar buttons placeholder debug painting
2024-02-06 11:30:11 +01:00
Karl Tauber
8f161b4b5a introduced FlatUIAction 2024-02-05 19:03:29 +01:00
Karl Tauber
c6338169f3 Testing: updated lafs.properties 2024-02-05 14:20:21 +01:00
Karl Tauber
6cea24ed9e GitHub Actions: updated versions of used actions 2024-02-05 11:34:58 +01:00
Karl Tauber
3d8eb9eb66 UIDefaultsKeysDump: exclude unused UI properties 2024-02-05 11:33:25 +01:00
Karl Tauber
a84aceb1ba Window decorations: improved caption hit testing to better support TabbedPane, SplitPane and ToolBar in title bar area (e.g. for fullWindowContent mode) 2024-02-04 16:30:38 +01:00
Karl Tauber
8adb7e3021 Native libraries: support Gradle cache when running in development environment (issue #800) 2024-02-02 19:02:56 +01:00
Karl Tauber
bc0d5dc9b5 Native libraries: publish flatlaf-<version>-no-natives.jar to Maven Central; this JAR is equal to flatlaf-<version>.jar, except that it does not contain the FlatLaf native libraries (issue #800) 2024-02-02 17:51:06 +01:00
Karl Tauber
1d935d6659 Window decorations: support fullWindowContent mode on Windows and Linux 2024-02-02 10:06:25 +01:00
Karl Tauber
445466acd0 Demo: removed toolbar button listeners used to test PR #779 2024-01-28 12:14:28 +01:00
Karl Tauber
30af74f806 macOS: setting window background (of undecorated window) to translucent color (alpha < 255) did not show the window translucent (issue #705) 2024-01-28 11:57:17 +01:00
Karl Tauber
16ddd100d3 RootPane: undone commits 7b248427f0 and c6d1ed91a7 because it seems to have no effect anymore
- on Windows with FlatLaf window decorations, maybe because of 7f02eb9cf0
- on Windows with standard window decorations, it seems not to work (still shows "white lines" @2.25x on Windows 10; Java 17)
- macOS and Linux do not support fractional scaling and "white lines" shown never be shown on that platforms
2024-01-27 11:19:18 +01:00
Karl Tauber
c946ec170d macOS window buttons spacing:
- uninstall when switching from FlatLaf to another Laf
- install when switching from another Laf to FlatLaf

(for PR #779)
2024-01-27 11:09:16 +01:00
Karl Tauber
ca514dd76e Merge PR #779: macOS: window title bar close/minimize/zoom buttons spacing 2024-01-25 14:06:38 +01:00
Karl Tauber
cf44a5c50b GitHub Actions: publish shapshots of PRs to Sonatype OSSRH 2024-01-25 13:26:31 +01:00
Karl Tauber
91b8c02c7f GitHub Actions: ignore changes to files that are not related to builds (e.g. readmes, etc.) 2024-01-24 19:23:47 +01:00
Karl Tauber
3465fa68b4 macOS window buttons spacing:
- renamed client property `MACOS_WINDOW_BUTTON_STYLE` to `MACOS_WINDOW_BUTTONS_SPACING`
- no longer allow value `true` for that client property
- enable using `MACOS_WINDOW_BUTTONS_SPACING` without `apple.awt.fullWindowContent`
- remove client property `FULL_WINDOW_CONTENT_BUTTONS_BOUNDS` when `apple.awt.fullWindowContent` is set to false or null
- added placeholder options `zeroInFullScreen`, `leftToRight` and `rightToLeft`
- hide close/min/max buttons during the transition from full-screen to non-full-screen to avoid that they "jump" when the nsToolbar is made visible
- fixed: full-screen listeners where added multiple times
- updated macOS native libraries
- added `FlatMacOSTest`
2024-01-22 00:31:40 +01:00
Karl Tauber
28278a75a7 macOS fullWindowContent mode:
- added title bar buttons placeholder
- added client property to root pane that contains title bar buttons bounds
- undone toolbar extensions from commit ea2447dcb7
2024-01-21 19:19:46 +01:00
Karl Tauber
f68a871dd6 macOS window button style: fixed javadoc 2024-01-12 22:49:49 +01:00
Karl Tauber
93d424cfe1 macOS native: added FlatNativeMacLibrary.windowToggleFullScreen() for easier testing 2024-01-12 22:49:49 +01:00
Karl Tauber
a1adde0888 macOS window button style: support NSWindowToolbarStyleUnified (availaible since macOS 11+; standard in macOS Finder, etc) to allow even larger space around close/minimize/zoom buttons 2024-01-12 22:48:31 +01:00
Karl Tauber
13528b49cb macOS large title bar: fixed wrong "main" JToolBar height and left inset after leaving full screen 2024-01-12 22:48:31 +01:00
Karl Tauber
f3be3f2d1c macOS large title bar: added client property FlatClientProperties.MACOS_LARGE_WINDOW_TITLE_BAR (or "FlatLaf.macOS.largeWindowTitleBar") 2024-01-12 22:48:31 +01:00
Karl Tauber
241fe855cc macOS large title bar: hide NSToolbar of window title bar when window becomes full screen 2024-01-12 22:47:33 +01:00
Karl Tauber
ea2447dcb7 macOS large title bar: main JToolBar automatically:
- uses height of macOS window title bar
- adds left insets for close/minimize/zoom buttons (except if full screen, where those buttons are hidden)
2024-01-12 22:47:33 +01:00
Karl Tauber
f40baed65e macOS large title bar: add NSToolbar to NSWindow 2024-01-12 22:45:54 +01:00
103 changed files with 4379 additions and 982 deletions

View File

@@ -9,6 +9,14 @@ on:
- '*'
tags:
- '[0-9]*'
paths-ignore:
- '**.md'
- '.*'
- '**/.settings/**'
- 'flatlaf-core/svg/**'
- 'flatlaf-testing/dumps/**'
- 'flatlaf-testing/misc/**'
- 'images/**'
jobs:
build:
@@ -24,22 +32,23 @@ jobs:
- 8
- 11 # LTS
- 17 # LTS
- 21 # LTS
toolchain: [""]
include:
- java: 17
toolchain: 21 # latest
- java: 21
toolchain: 22 # latest
steps:
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1
- uses: gradle/wrapper-validation-action@v2
if: matrix.java == '8'
- name: Setup Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: temurin # Java 8, 11 and 17 are pre-installed on ubuntu-latest
distribution: temurin # Java 8, 11, 17 and 21 are pre-installed on ubuntu-latest
cache: gradle
- name: Check with Error Prone

View File

@@ -13,6 +13,8 @@ on:
- 'flatlaf-fonts/**'
- '.github/workflows/fonts.yml'
- 'gradle/wrapper/gradle-wrapper.properties'
- '!**.md'
- '!**/.settings/**'
jobs:
Fonts:

View File

@@ -13,6 +13,8 @@ on:
- 'flatlaf-natives/**'
- '.github/workflows/natives.yml'
- 'gradle/wrapper/gradle-wrapper.properties'
- '!**.md'
- '!**/.settings/**'
jobs:
Natives:
@@ -28,7 +30,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1
- uses: gradle/wrapper-validation-action@v2
- name: Setup Java 11
uses: actions/setup-java@v4

37
.github/workflows/pr-snapshots.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
name: PR Snapshots
on:
pull_request:
paths-ignore:
- '**.md'
- '.*'
- '**/.settings/**'
- 'flatlaf-core/svg/**'
- 'flatlaf-testing/dumps/**'
- 'flatlaf-testing/misc/**'
- 'images/**'
jobs:
snapshot:
runs-on: ubuntu-latest
if: github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v4
- name: Setup Java 11
uses: actions/setup-java@v4
with:
java-version: 11
distribution: temurin # pre-installed on ubuntu-latest
cache: gradle
- name: Publish PR snapshot to oss.sonatype.org
run: >
./gradlew publish -PskipFonts -Dorg.gradle.internal.publish.checksums.insecure=true -Dorg.gradle.parallel=false
-Pgithub.event.pull_request.number=${{ github.event.pull_request.number }}
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}

View File

@@ -1,18 +1,66 @@
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).
## 3.4.1
#### Fixed bugs
- SplitPane: Update divider when client property `JSplitPane.expandableSide`
changed.
- TabbedPane: Fixed swapped back and forward scroll buttons when using
`TabbedPane.scrollButtonsPlacement = trailing` (regression in FlatLaf 3.3).
- Fixed missing window top border on Windows 10 in "full window content" mode.
(issue 809)
- Extras:
- `FlatSVGIcon` color filters now support linear gradients. (PR #817)
- `FlatSVGIcon`: Use log level `CONFIG` instead of `SEVERE` and allow
disabling logging. (issue #823)
- Added support for `JSplitPane.expandableSide` client property to
`FlatSplitPane`.
- Native libraries: Added API version check to test whether native library
matches the JAR (bad builds could e.g. ship a newer JAR with an older
incompatible native library) and to test whether native methods can be invoked
(some security software allows loading native library but blocks method
invocation).
- macOS: Fixed crash when running in WebSwing. (issue #826; regression in 3.4)
#### Incompatibilities
- File names of custom properties files for nested Laf classes now must include
name of enclosing class name. E.g. nested Laf class `IntelliJTheme.ThemeLaf`
used `ThemeLaf.properties` in previous versions, but now needs to be named
`IntelliJTheme$ThemeLaf.properties`.
## 3.4
#### New features and improvements
- FlatLaf window decorations (Windows 10/11 and Linux): Support "full window
content" mode, which allows you to extend the content into the window title
bar. (PR #801)
- macOS: Support larger window title bar close/minimize/zoom buttons spacing in
[full window content](https://www.formdev.com/flatlaf/macos/#full_window_content)
mode and introduced "buttons placeholder". (PR #779)
- Native libraries:
- System property `flatlaf.nativeLibraryPath` now supports loading native
libraries named the same as on Maven central.
- Published `flatlaf-<version>-no-natives.jar` to Maven Central. This JAR is
equal to `flatlaf-<version>.jar`, except that it does not contain the
FlatLaf native libraries. The Maven "classifier" to use this JAR is
`no-natives`. You need to distribute the FlatLaf native libraries with your
application.
See https://www.formdev.com/flatlaf/native-libraries/ for more details.
- Improved log messages for loading fails.
- Fonts: Updated **Inter** to
[v4.0](https://github.com/rsms/inter/releases/tag/v4.0).
- Table: Select all text in cell editor when starting editing using `F2` key.
(issue 652)
#### Fixed bugs
- macOS: Setting window background (of undecorated window) to translucent color
(alpha < 255) did not show the window translucent. (issue #705)
- JIDE CommandMenuBar: Fixed `ClassCastException` when JIDE command bar displays
`JideMenu` in popup. (PR #794)

View File

@@ -311,6 +311,9 @@ Applications using FlatLaf
- ![New](images/new.svg) ![Sponsor](images/sponsor.svg)
[BGBlitz](https://www.bgblitz.com/) (**commercial**) - professional Backgammon
- ![New](images/new.svg) [MCreator](https://github.com/MCreator/MCreator) -
software used to make Minecraft Java Edition mods, Minecraft Bedrock Edition Add-Ons,
and data packs without programming knowledge
- ![New](images/new.svg) [MapTool](https://github.com/RPTools/maptool) - virtual
Tabletop for playing role-playing games
- [MegaMek](https://github.com/MegaMek/megamek),

View File

@@ -18,6 +18,12 @@ import net.ltgt.gradle.errorprone.errorprone
version = property( if( hasProperty( "release" ) ) "flatlaf.releaseVersion" else "flatlaf.developmentVersion" ) as String
// for PR snapshots change version to 'PR-<pr_number>-SNAPSHOT'
val pullRequestNumber = findProperty( "github.event.pull_request.number" )
if( pullRequestNumber != null )
version = "PR-${pullRequestNumber}-SNAPSHOT"
allprojects {
version = rootProject.version

View File

@@ -27,7 +27,7 @@ library {
}
with( linkTask.get() ) {
if( name.contains( "Release" ) )
debuggable.set( false )
debuggable = false
}
}
}

View File

@@ -44,34 +44,34 @@ publishing {
pom {
afterEvaluate {
this@pom.name.set( extension.name )
this@pom.description.set( extension.description )
this@pom.name = extension.name
this@pom.description = extension.description
}
url.set( "https://github.com/JFormDesigner/FlatLaf" )
url = "https://github.com/JFormDesigner/FlatLaf"
licenses {
license {
name.set( "The Apache License, Version 2.0" )
url.set( "https://www.apache.org/licenses/LICENSE-2.0.txt" )
name = "The Apache License, Version 2.0"
url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
}
}
developers {
developer {
name.set( "Karl Tauber" )
organization.set( "FormDev Software GmbH" )
organizationUrl.set( "https://www.formdev.com/" )
name = "Karl Tauber"
organization = "FormDev Software GmbH"
organizationUrl = "https://www.formdev.com/"
}
}
scm {
connection.set( "scm:git:git://github.com/JFormDesigner/FlatLaf.git" )
url.set( "https://github.com/JFormDesigner/FlatLaf" )
connection = "scm:git:git://github.com/JFormDesigner/FlatLaf.git"
url = "https://github.com/JFormDesigner/FlatLaf"
}
issueManagement {
system.set( "GitHub" )
url.set( "https://github.com/JFormDesigner/FlatLaf/issues" )
system = "GitHub"
url = "https://github.com/JFormDesigner/FlatLaf/issues"
}
}
@@ -124,7 +124,7 @@ tasks.withType<Sign>().configureEach {
}
// check whether parallel build is enabled
tasks.withType<PublishToMavenRepository>().configureEach {
tasks.withType<AbstractPublishToMaven>().configureEach {
doFirst {
if( System.getProperty( "org.gradle.parallel" ) == "true" )
throw RuntimeException( "Publishing does not work correctly with enabled parallel build. Disable parallel build with VM option '-Dorg.gradle.parallel=false'." )

View File

@@ -21,6 +21,6 @@ plugins {
val toolchainJavaVersion = System.getProperty( "toolchain" )
if( !toolchainJavaVersion.isNullOrEmpty() ) {
java.toolchain {
languageVersion.set( JavaLanguageVersion.of( toolchainJavaVersion ) )
languageVersion = JavaLanguageVersion.of( toolchainJavaVersion )
}
}

View File

@@ -27,8 +27,8 @@ plugins {
val sigtest = configurations.create( "sigtest" )
dependencies {
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
testImplementation( libs.junit )
testRuntimeOnly( libs.junit.launcher )
// https://github.com/jtulach/netbeans-apitest
sigtest( libs.sigtest )
@@ -42,11 +42,11 @@ java {
tasks {
compileJava {
// generate JNI headers
options.headerOutputDirectory.set( layout.buildDirectory.dir( "generated/jni-headers" ) )
options.headerOutputDirectory = layout.buildDirectory.dir( "generated/jni-headers" )
}
jar {
archiveBaseName.set( "flatlaf" )
archiveBaseName = "flatlaf"
doLast {
ReorderJarEntries.reorderJarEntries( outputs.files.singleFile );
@@ -54,11 +54,32 @@ tasks {
}
named<Jar>( "sourcesJar" ) {
archiveBaseName.set( "flatlaf" )
archiveBaseName = "flatlaf"
}
named<Jar>( "javadocJar" ) {
archiveBaseName.set( "flatlaf" )
archiveBaseName = "flatlaf"
}
register<Zip>( "jarNoNatives" ) {
group = "build"
dependsOn( "jar" )
archiveBaseName = "flatlaf"
archiveClassifier = "no-natives"
archiveExtension = "jar"
destinationDirectory = layout.buildDirectory.dir( "libs" )
from( zipTree( jar.get().archiveFile.get().asFile ) )
exclude( "com/formdev/flatlaf/natives/**" )
}
withType<AbstractPublishToMaven>().configureEach {
dependsOn( "jarNoNatives" )
}
withType<Sign>().configureEach {
dependsOn( "jarNoNatives" )
}
check {
@@ -127,6 +148,8 @@ flatlafPublish {
val natives = "src/main/resources/com/formdev/flatlaf/natives"
nativeArtifacts = listOf(
NativeArtifact( tasks.getByName( "jarNoNatives" ).outputs.files.asPath, "no-natives", "jar" ),
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),

View File

@@ -1,5 +1,5 @@
#Signature file v4.1
#Version 3.3
#Version 3.4
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
@@ -12,7 +12,13 @@ fld public final static java.lang.String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarBu
fld public final static java.lang.String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner"
fld public final static java.lang.String COMPONENT_ROUND_RECT = "JComponent.roundRect"
fld public final static java.lang.String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption"
fld public final static java.lang.String FULL_WINDOW_CONTENT = "FlatLaf.fullWindowContent"
fld public final static java.lang.String FULL_WINDOW_CONTENT_BUTTONS_BOUNDS = "FlatLaf.fullWindowContent.buttonsBounds"
fld public final static java.lang.String FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER = "FlatLaf.fullWindowContent.buttonsPlaceholder"
fld public final static java.lang.String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight"
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING = "FlatLaf.macOS.windowButtonsSpacing"
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING_LARGE = "large"
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING_MEDIUM = "medium"
fld public final static java.lang.String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"
fld public final static java.lang.String MINIMUM_HEIGHT = "JComponent.minimumHeight"
fld public final static java.lang.String MINIMUM_WIDTH = "JComponent.minimumWidth"

View File

@@ -102,6 +102,17 @@ public interface FlatClientProperties
*/
String BUTTON_TYPE_BORDERLESS = "borderless";
/**
* Specifies whether the button preferred size will be made square (quadratically).
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String SQUARE_SIZE = "JButton.squareSize";
//---- JCheckBox ----------------------------------------------------------
/**
* Specifies selected state of a checkbox.
* <p>
@@ -118,14 +129,6 @@ public interface FlatClientProperties
*/
String SELECTED_STATE_INDETERMINATE = "indeterminate";
/**
* Specifies whether the button preferred size will be made square (quadratically).
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String SQUARE_SIZE = "JButton.squareSize";
//---- JComponent ---------------------------------------------------------
@@ -257,19 +260,116 @@ public interface FlatClientProperties
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
/**
* Specifies whether a component in an embedded menu bar should behave as caption
* Specifies whether a component shown in a window title bar area should behave as caption
* (left-click allows moving window, right-click shows window system menu).
* The component does not receive mouse pressed/released/clicked/dragged events,
* The caption component does not receive mouse pressed/released/clicked/dragged events,
* but it gets mouse entered/exited/moved events.
* <p>
* Since 3.4, this client property also supports using a function that can check
* whether a given location in the component should behave as caption.
* Useful for components that do not use mouse input on whole component bounds.
*
* <pre>{@code
* myComponent.putClientProperty( "JComponent.titleBarCaption",
* (Function<Point, Boolean>) pt -> {
* // parameter pt contains mouse location (in myComponent coordinates)
* // return true if the component is not interested in mouse input at the given location
* // return false if the component wants process mouse input at the given location
* // return null if the component children should be checked
* return ...; // check here
* } );
* }</pre>
* <b>Warning</b>:
* <ul>
* <li>This function is invoked often when mouse is moved over window title bar area
* and should therefore return quickly.
* <li>This function is invoked on 'AWT-Windows' thread (not 'AWT-EventQueue' thread)
* while processing Windows messages.
* It <b>must not</b> change any component property or layout because this could cause a dead lock.
* </ul>
* <p>
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
* <strong>Value type</strong> {@link java.lang.Boolean} or {@link java.util.function.Function}&lt;Point, Boolean&gt;
*
* @since 2.5
*/
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
//---- Panel --------------------------------------------------------------
/**
* Marks the panel as placeholder for the iconfify/maximize/close buttons
* in fullWindowContent mode. See {@link #FULL_WINDOW_CONTENT}.
* <p>
* If fullWindowContent mode is enabled, the preferred size of the panel is equal
* to the size of the iconfify/maximize/close buttons. Otherwise is is {@code 0,0}.
* <p>
* You're responsible to layout that panel at the top-left or top-right corner,
* depending on platform, where the iconfify/maximize/close buttons are located.
* <p>
* Syntax of the value string is: {@code "win|mac [horizontal|vertical] [zeroInFullScreen] [leftToRight|rightToLeft]"}.
* <p>
* The string must start with {@code "win"} (for Windows or Linux) or
* with {@code "mac"} (for macOS) and specifies the platform where the placeholder
* should be used. On macOS, you need the placeholder in the top-left corner,
* but on Windows/Linux you need it in the top-right corner. So if your application supports
* fullWindowContent mode on both platforms, you can add two placeholders to your layout
* and FlatLaf automatically uses only one of them. The other gets size {@code 0,0}.
* <p>
* Optionally, you can append following options to the value string (separated by space characters):
* <ul>
* <li>{@code "horizontal"} - preferred height is zero
* <li>{@code "vertical"} - preferred width is zero
* <li>{@code "zeroInFullScreen"} - in full-screen mode on macOS, preferred size is {@code 0,0}
* <li>{@code "leftToRight"} - in right-to-left component orientation, preferred size is {@code 0,0}
* <li>{@code "rightToLeft"} - in left-to-right component orientation, preferred size is {@code 0,0}
* </ul>
*
* Example for adding placeholder to top-left corner on macOS:
* <pre>{@code
* JPanel placeholder = new JPanel();
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
*
* JToolBar toolBar = new JToolBar();
* // add tool bar items
*
* JPanel toolBarPanel = new JPanel( new BorderLayout() );
* toolBarPanel.add( placeholder, BorderLayout.WEST );
* toolBarPanel.add( toolBar, BorderLayout.CENTER );
*
* frame.getContentPane().add( toolBarPanel, BorderLayout.NORTH );
* }</pre>
*
* Or add placeholder as first item to the tool bar:
* <pre>{@code
* JPanel placeholder = new JPanel();
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
*
* JToolBar toolBar = new JToolBar();
* toolBar.add( placeholder );
* // add tool bar items
*
* frame.getContentPane().add( toolBar, BorderLayout.NORTH );
* }</pre>
*
* If a tabbed pane is located at the top, you can add the placeholder
* as leading component to that tabbed pane:
* <pre>{@code
* JPanel placeholder = new JPanel();
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
*
* tabbedPane.putClientProperty( FlatClientProperties.TABBED_PANE_LEADING_COMPONENT, placeholder );
* }</pre>
* <p>
* <strong>Component</strong> {@link javax.swing.JPanel}<br>
* <strong>Value type</strong> {@link java.lang.String}
*
* @since 3.4
*/
String FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER = "FlatLaf.fullWindowContent.buttonsPlaceholder";
//---- Popup --------------------------------------------------------------
/**
@@ -388,6 +488,46 @@ public interface FlatClientProperties
*/
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
/**
* Specifies whether the content pane (and the glass pane) should be extended
* into the window title bar area
* (requires enabled window decorations). Default is {@code false}.
* <p>
* On macOS, use client property {@code apple.awt.fullWindowContent}
* (see <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">macOS Full window content</a>).
* <p>
* Setting this enables/disables full window content
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
* <p>
* If {@code true}, the content pane (and the glass pane) is extended into
* the title bar area. The window icon and title are hidden.
* Only the iconfify/maximize/close buttons stay visible in the upper right corner
* (and overlap the content pane).
* <p>
* The user can left-click-and-drag on the title bar area to move the window,
* except when clicking on a component that processes mouse events (e.g. buttons or menus).
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 3.4
*/
String FULL_WINDOW_CONTENT = "FlatLaf.fullWindowContent";
/**
* Contains the current bounds of the iconfify/maximize/close buttons
* (in root pane coordinates) if fullWindowContent mode is enabled.
* Otherwise its value is {@code null}.
* <p>
* <b>Note</b>: Do not set this client property. It is set by FlatLaf.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.awt.Rectangle}
*
* @since 3.4
*/
String FULL_WINDOW_CONTENT_BUTTONS_BOUNDS = "FlatLaf.fullWindowContent.buttonsBounds";
/**
* Specifies whether the window icon should be shown in the window title bar
* (requires enabled window decorations). Default is UI property {@code TitlePane.showIcon}.
@@ -847,7 +987,7 @@ public interface FlatClientProperties
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link SwingConstants#LEADING} (default)
* {@link SwingConstants#LEADING} (default),
* {@link SwingConstants#TRAILING},
* {@link SwingConstants#CENTER},
* {@link #TABBED_PANE_ALIGN_LEADING} (default),
@@ -959,7 +1099,7 @@ public interface FlatClientProperties
* <strong>Allowed Values</strong>
* {@link SwingConstants#LEFT},
* {@link SwingConstants#RIGHT},
* {@link #TABBED_PANE_TAB_ROTATION_NONE}, (default)
* {@link #TABBED_PANE_TAB_ROTATION_NONE} (default),
* {@link #TABBED_PANE_TAB_ROTATION_AUTO},
* {@link #TABBED_PANE_TAB_ROTATION_LEFT} or
* {@link #TABBED_PANE_TAB_ROTATION_RIGHT}
@@ -1208,8 +1348,8 @@ public interface FlatClientProperties
* <p>
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br>
* <strong>SupportedValues:</strong>
* {@link SwingConstants#BOTTOM} (default)
* <strong>Allowed Values</strong>
* {@link SwingConstants#BOTTOM} (default),
* {@link SwingConstants#TOP},
* {@link SwingConstants#LEFT} or
* {@link SwingConstants#RIGHT}
@@ -1263,6 +1403,44 @@ public interface FlatClientProperties
String TREE_PAINT_SELECTION = "JTree.paintSelection";
//---- macOS --------------------------------------------------------------
/**
* Specifies the spacing around the macOS window close/minimize/zoom buttons.
* Useful if <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">full window content</a>
* is enabled.
* <p>
* (requires macOS 10.14+ for "medium" spacing and macOS 11+ for "large" spacing, requires Java 17+)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #MACOS_WINDOW_BUTTONS_SPACING_MEDIUM} or
* {@link #MACOS_WINDOW_BUTTONS_SPACING_LARGE} (requires macOS 11+)
*
* @since 3.4
*/
String MACOS_WINDOW_BUTTONS_SPACING = "FlatLaf.macOS.windowButtonsSpacing";
/**
* Add medium spacing around the macOS window close/minimize/zoom buttons.
*
* @see #MACOS_WINDOW_BUTTONS_SPACING
* @since 3.4
*/
String MACOS_WINDOW_BUTTONS_SPACING_MEDIUM = "medium";
/**
* Add large spacing around the macOS window close/minimize/zoom buttons.
* <p>
* (requires macOS 11+; "medium" is used on older systems)
*
* @see #MACOS_WINDOW_BUTTONS_SPACING
* @since 3.4
*/
String MACOS_WINDOW_BUTTONS_SPACING_LARGE = "large";
//---- helper methods -----------------------------------------------------
/**

View File

@@ -48,7 +48,7 @@ public abstract class FlatDefaultsAddon
public InputStream getDefaults( Class<?> lafClass ) {
Class<?> addonClass = this.getClass();
String propertiesName = '/' + addonClass.getPackage().getName().replace( '.', '/' )
+ '/' + lafClass.getSimpleName() + ".properties";
+ '/' + UIDefaultsLoader.simpleClassName( lafClass ) + ".properties";
return addonClass.getResourceAsStream( propertiesName );
}

View File

@@ -161,7 +161,7 @@ class UIDefaultsLoader
classLoader = FlatLaf.class.getClassLoader();
for( Class<?> lafClass : lafClasses ) {
String propertiesName = packageName + '/' + lafClass.getSimpleName() + ".properties";
String propertiesName = packageName + '/' + simpleClassName( lafClass ) + ".properties";
try( InputStream in = classLoader.getResourceAsStream( propertiesName ) ) {
if( in != null )
properties.load( in );
@@ -171,7 +171,7 @@ class UIDefaultsLoader
// load from package URL
URL packageUrl = (URL) source;
for( Class<?> lafClass : lafClasses ) {
URL propertiesUrl = new URL( packageUrl + lafClass.getSimpleName() + ".properties" );
URL propertiesUrl = new URL( packageUrl + simpleClassName( lafClass ) + ".properties" );
try( InputStream in = propertiesUrl.openStream() ) {
properties.load( in );
@@ -183,7 +183,7 @@ class UIDefaultsLoader
// load from folder
File folder = (File) source;
for( Class<?> lafClass : lafClasses ) {
File propertiesFile = new File( folder, lafClass.getSimpleName() + ".properties" );
File propertiesFile = new File( folder, simpleClassName( lafClass ) + ".properties" );
if( !propertiesFile.isFile() )
continue;
@@ -294,6 +294,14 @@ class UIDefaultsLoader
}
}
/**
* Similar to Class.getSimpleName(), but includes enclosing class for nested classes.
*/
static String simpleClassName( Class<?> cls ) {
String className = cls.getName();
return className.substring( className.lastIndexOf( '.' ) + 1 );
}
static void logParseError( String key, String value, RuntimeException ex, boolean severe ) {
String message = "FlatLaf: Failed to parse: '" + key + '=' + value + '\'';
if( severe )

View File

@@ -429,7 +429,7 @@ public class FlatFileChooserUI
iconFunction = (Function<File, Icon>) UIManager.get( "FileChooser.shortcuts.iconFunction" );
FileSystemView fsv = fc.getFileSystemView();
File[] files = getChooserShortcutPanelFiles( fsv );
File[] files = JavaCompatibility2.getChooserShortcutPanelFiles( fsv );
if( filesFunction != null )
files = filesFunction.apply( files );
@@ -498,32 +498,6 @@ public class FlatFileChooserUI
return button;
}
protected File[] getChooserShortcutPanelFiles( FileSystemView fsv ) {
try {
if( SystemInfo.isJava_12_orLater ) {
Method m = fsv.getClass().getMethod( "getChooserShortcutPanelFiles" );
File[] files = (File[]) m.invoke( fsv );
// on macOS and Linux, files consists only of the user home directory
if( files.length == 1 && files[0].equals( new File( System.getProperty( "user.home" ) ) ) )
files = new File[0];
return files;
} else if( SystemInfo.isWindows ) {
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
Method m = cls.getMethod( "get", String.class );
return (File[]) m.invoke( null, "fileChooserShortcutPanelFolders" );
}
} catch( IllegalAccessException ex ) {
// do not log because access may be denied via VM option '--illegal-access=deny'
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
// fallback
return new File[0];
}
protected String getDisplayName( FileSystemView fsv, File file ) {
if( displayNameFunction != null ) {
String name = displayNameFunction.apply( file );

View File

@@ -27,7 +27,6 @@ import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
@@ -39,7 +38,6 @@ import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicMenuBarUI;
@@ -144,12 +142,10 @@ public class FlatMenuBarUI
protected void installKeyboardActions() {
super.installKeyboardActions();
// get shared action map, used for all menu bars
ActionMap map = SwingUtilities.getUIActionMap( menuBar );
if( map == null ) {
map = new ActionMapUIResource();
SwingUtilities.replaceUIActionMap( menuBar, map );
}
map.put( "takeFocus", new TakeFocus() );
if( map != null && !(map.get( "takeFocus" ) instanceof TakeFocusAction) )
map.put( "takeFocus", new TakeFocusAction( "takeFocus" ) );
}
/** @since 2 */
@@ -365,16 +361,20 @@ public class FlatMenuBarUI
}
}
//---- class TakeFocus ----------------------------------------------------
//---- class TakeFocusAction ----------------------------------------------
/**
* Activates the menu bar and shows mnemonics.
* On Windows, the popup of the first menu is not shown.
* On other platforms, the popup of the first menu is shown.
*/
private static class TakeFocus
extends AbstractAction
private static class TakeFocusAction
extends FlatUIAction
{
TakeFocusAction( String name ) {
super( name );
}
@Override
public void actionPerformed( ActionEvent e ) {
JMenuBar menuBar = (JMenuBar) e.getSource();

View File

@@ -22,6 +22,7 @@ import java.security.CodeSource;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.NativeLibrary;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
@@ -36,16 +37,18 @@ class FlatNativeLibrary
private static boolean initialized;
private static NativeLibrary nativeLibrary;
private native static int getApiVersion();
/**
* Loads native library (if available) and returns whether loaded successfully.
* Returns {@code false} if no native library is available.
*/
static synchronized boolean isLoaded() {
initialize();
static synchronized boolean isLoaded( int apiVersion ) {
initialize( apiVersion );
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
}
private static void initialize() {
private static void initialize( int apiVersion ) {
if( initialized )
return;
initialized = true;
@@ -103,7 +106,26 @@ class FlatNativeLibrary
return; // no native library available for current OS or CPU architecture
// load native library
nativeLibrary = createNativeLibrary( classifier, ext );
NativeLibrary nativeLibrary = createNativeLibrary( classifier, ext );
if( !nativeLibrary.isLoaded() )
return;
// check API version (and check whether library works)
try {
int actualApiVersion = getApiVersion();
if( actualApiVersion != apiVersion ) {
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Wrong API version in native library (expected "
+ apiVersion + ", actual " + actualApiVersion + "). Ignoring native library.", null );
return;
}
} catch( Throwable ex ) {
// could be a UnsatisfiedLinkError in case that loading native library
// from temp directory was blocked by some OS security mechanism
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to get API version of native library. Ignoring native library.", ex );
return;
}
FlatNativeLibrary.nativeLibrary = nativeLibrary;
}
private static NativeLibrary createNativeLibrary( String classifier, String ext ) {
@@ -117,9 +139,9 @@ class FlatNativeLibrary
if( library.isLoaded() )
return library;
LoggingFacade.INSTANCE.logSevere( "Did not find library '" + System.mapLibraryName( libraryName )
+ "' in java.library.path '" + System.getProperty( "java.library.path" )
+ "', using extracted library instead", null );
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Native library '" + System.mapLibraryName( libraryName )
+ "' not found in java.library.path '" + System.getProperty( "java.library.path" )
+ "'. Using extracted native library instead.", null );
} else {
// try standard library naming scheme
// (same as in flatlaf.jar in package 'com/formdev/flatlaf/natives')
@@ -138,11 +160,11 @@ class FlatNativeLibrary
return new NativeLibrary( libraryFile2, true );
}
LoggingFacade.INSTANCE.logSevere( "Did not find library '"
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Native library '"
+ libraryFile.getName()
+ (libraryName2 != null ? ("' or '" + libraryName2) : "")
+ "' in '" + libraryFile.getParentFile().getAbsolutePath()
+ "', using extracted library instead", null );
+ "' not found in '" + libraryFile.getParentFile().getAbsolutePath()
+ "'. Using extracted native library instead.", null );
}
}
@@ -177,21 +199,39 @@ class FlatNativeLibrary
// build library file
String libraryName = buildLibraryName( jarFile, classifier, ext );
File parent = jarFile.getParentFile();
File jarDir = jarFile.getParentFile();
// check whether native library exists in same directory as jar
File libraryFile = new File( parent, libraryName );
File libraryFile = new File( jarDir, libraryName );
if( libraryFile.isFile() )
return libraryFile;
// if jar is in "lib" directory, then also check whether native library exists
// in "../bin" directory
if( parent.getName().equalsIgnoreCase( "lib" ) ) {
libraryFile = new File( parent.getParentFile(), "bin/" + libraryName );
if( jarDir.getName().equalsIgnoreCase( "lib" ) ) {
libraryFile = new File( jarDir.getParentFile(), "bin/" + libraryName );
if( libraryFile.isFile() )
return libraryFile;
}
// special case: support Gradle cache when running in development environment
// <user-home>/.gradle/caches/modules-2/files-2.1/com.formdev/flatlaf/<version>/<hash-1>/flatlaf-<version>.jar
// <user-home>/.gradle/caches/modules-2/files-2.1/com.formdev/flatlaf/<version>/<hash-2>/flatlaf-<version>-windows-x86_64.dll
String path = jarDir.getAbsolutePath().replace( '\\', '/' );
if( path.contains( "/.gradle/caches/" ) ) {
File versionDir = jarDir.getParentFile();
if( libraryName.contains( versionDir.getName() ) ) {
File[] dirs = versionDir.listFiles();
if( dirs != null ) {
for( File dir : dirs ) {
libraryFile = new File( dir, libraryName );
if( libraryFile.isFile() )
return libraryFile;
}
}
}
}
// native library not found
return null;
}
@@ -224,6 +264,10 @@ class FlatNativeLibrary
private static String buildLibraryName( File jarFile, String classifier, String ext ) {
String jarName = jarFile.getName();
String jarBasename = jarName.substring( 0, jarName.lastIndexOf( '.' ) );
// remove classifier "no-natives" (if used)
jarBasename = StringUtils.removeTrailing( jarBasename, "-no-natives" );
return jarBasename
+ (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf")
+ '-' + classifier + '.' + ext;

View File

@@ -35,6 +35,8 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
class FlatNativeLinuxLibrary
{
private static int API_VERSION_LINUX = 3001;
/**
* Checks whether native library is loaded/available.
* <p>
@@ -42,7 +44,7 @@ class FlatNativeLinuxLibrary
* method of this class. Otherwise, the native library may not be loaded.
*/
static boolean isLoaded() {
return SystemInfo.isLinux && FlatNativeLibrary.isLoaded();
return SystemInfo.isLinux && FlatNativeLibrary.isLoaded( API_VERSION_LINUX );
}
// direction for _NET_WM_MOVERESIZE message

View File

@@ -16,7 +16,9 @@
package com.formdev.flatlaf.ui;
import java.awt.Rectangle;
import java.awt.Window;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Native methods for macOS.
@@ -42,6 +44,8 @@ import java.awt.Window;
*/
public class FlatNativeMacLibrary
{
private static int API_VERSION_MACOS = 2001;
/**
* Checks whether native library is loaded/available.
* <p>
@@ -49,8 +53,19 @@ public class FlatNativeMacLibrary
* method of this class. Otherwise, the native library may not be loaded.
*/
public static boolean isLoaded() {
return FlatNativeLibrary.isLoaded();
return SystemInfo.isMacOS && FlatNativeLibrary.isLoaded( API_VERSION_MACOS );
}
public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor );
/** @since 3.4 */
public static final int
BUTTONS_SPACING_DEFAULT = 0,
BUTTONS_SPACING_MEDIUM = 1,
BUTTONS_SPACING_LARGE = 2;
/** @since 3.4 */ public native static boolean setWindowButtonsSpacing( Window window, int buttonsSpacing );
/** @since 3.4 */ public native static Rectangle getWindowButtonsBounds( Window window );
/** @since 3.4 */ public native static boolean isWindowFullScreen( Window window );
/** @since 3.4 */ public native static boolean toggleWindowFullScreen( Window window );
}

View File

@@ -21,11 +21,12 @@ import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.function.Predicate;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JRootPane;
@@ -218,13 +219,13 @@ public class FlatNativeWindowBorder
}
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
Predicate<Point> captionHitTestCallback, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
{
if( !isSupported() )
return;
nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots,
nativeProvider.updateTitleBarInfo( window, titleBarHeight, captionHitTestCallback,
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
}
@@ -270,7 +271,7 @@ public class FlatNativeWindowBorder
{
boolean hasCustomDecoration( Window window );
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
void updateTitleBarInfo( Window window, int titleBarHeight, Predicate<Point> captionHitTestCallback,
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
Rectangle closeButtonBounds );

View File

@@ -30,6 +30,8 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
public class FlatNativeWindowsLibrary
{
private static int API_VERSION_WINDOWS = 1001;
private static long osBuildNumber = Long.MIN_VALUE;
/**
@@ -39,7 +41,7 @@ public class FlatNativeWindowsLibrary
* method of this class. Otherwise, the native library may not be loaded.
*/
public static boolean isLoaded() {
return SystemInfo.isWindows && FlatNativeLibrary.isLoaded();
return SystemInfo.isWindows && FlatNativeLibrary.isLoaded( API_VERSION_WINDOWS );
}
/**

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.ui;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.beans.PropertyChangeEvent;
@@ -23,6 +24,7 @@ import java.beans.PropertyChangeListener;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPanelUI;
import com.formdev.flatlaf.FlatClientProperties;
@@ -69,6 +71,8 @@ public class FlatPanelUI
super.installUI( c );
c.addPropertyChangeListener( this );
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
FullWindowContentSupport.registerPlaceholder( c );
installStyle( (JPanel) c );
}
@@ -78,10 +82,20 @@ public class FlatPanelUI
super.uninstallUI( c );
c.removePropertyChangeListener( this );
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
FullWindowContentSupport.unregisterPlaceholder( c );
oldStyleValues = null;
}
@Override
protected void installDefaults( JPanel p ) {
super.installDefaults( p );
if( p.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
LookAndFeel.installProperty( p, "opaque", false );
}
/** @since 2.0.1 */
@Override
public void propertyChange( PropertyChangeEvent e ) {
@@ -98,6 +112,17 @@ public class FlatPanelUI
c.revalidate();
c.repaint();
break;
case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER:
JPanel p = (JPanel) e.getSource();
if( e.getOldValue() != null )
FullWindowContentSupport.unregisterPlaceholder( p );
if( e.getNewValue() != null )
FullWindowContentSupport.registerPlaceholder( p );
// make panel non-opaque for placeholders
LookAndFeel.installProperty( p, "opaque", e.getNewValue() == null );
break;
}
}
@@ -162,4 +187,19 @@ public class FlatPanelUI
paint( g, c );
}
@Override
public Dimension getPreferredSize( JComponent c ) {
Object value = c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER );
if( value != null )
return FullWindowContentSupport.getPlaceholderPreferredSize( c, (String) value );
return super.getPreferredSize( c );
}
@Override
public void paint( Graphics g, JComponent c ) {
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
FullWindowContentSupport.debugPaint( g, c );
}
}

View File

@@ -28,8 +28,6 @@ import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
import java.awt.Window;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@@ -41,7 +39,6 @@ import javax.swing.JLayeredPane;
import javax.swing.JMenuBar;
import javax.swing.JRootPane;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.BorderUIResource;
@@ -50,6 +47,7 @@ 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;
@@ -87,8 +85,8 @@ public class FlatRootPaneUI
private Object nativeWindowBorderData;
private LayoutManager oldLayout;
private PropertyChangeListener ancestorListener;
private ComponentListener componentListener;
private ComponentListener macFullWindowContentListener;
private PropertyChangeListener macWindowBackgroundListener;
public static ComponentUI createUI( JComponent c ) {
return new FlatRootPaneUI();
@@ -106,6 +104,7 @@ public class FlatRootPaneUI
installBorder();
installNativeWindowBorder();
macInstallFullWindowContentSupport();
}
protected void installBorder() {
@@ -122,6 +121,7 @@ public class FlatRootPaneUI
uninstallNativeWindowBorder();
uninstallClientDecorations();
macUninstallFullWindowContentSupport();
rootPane = null;
}
@@ -155,6 +155,8 @@ public class FlatRootPaneUI
if( background == null || background instanceof UIResource )
parent.setBackground( UIManager.getColor( "control" ) );
}
macClearBackgroundForTranslucentWindow( c );
}
@Override
@@ -174,55 +176,20 @@ public class FlatRootPaneUI
protected void installListeners( JRootPane root ) {
super.installListeners( root );
if( SystemInfo.isJava_9_orLater ) {
// On HiDPI screens, where scaling is used, there may be white lines on the
// bottom and on the right side of the window when it is initially shown.
// This is very disturbing in dark themes, but hard to notice in light themes.
// Seems to be a rounding issue when Swing adds dirty region of window
// using RepaintManager.nativeAddDirtyRegion().
//
// Note: Not using a HierarchyListener here, which would be much easier,
// because this causes problems with mouse clicks in heavy-weight popups.
// Instead, add a listener to the root pane that waits until it is added
// to a window, then add a component listener to the window.
// See: https://github.com/JFormDesigner/FlatLaf/issues/371
ancestorListener = e -> {
Object oldValue = e.getOldValue();
Object newValue = e.getNewValue();
if( newValue instanceof Window ) {
if( componentListener == null ) {
componentListener = new ComponentAdapter() {
@Override
public void componentShown( ComponentEvent e ) {
// add whole root pane to dirty regions when window is initially shown
root.getParent().repaint( root.getX(), root.getY(), root.getWidth(), root.getHeight() );
}
};
}
((Window)newValue).addComponentListener( componentListener );
} else if( newValue == null && oldValue instanceof Window ) {
if( componentListener != null )
((Window)oldValue).removeComponentListener( componentListener );
}
};
root.addPropertyChangeListener( "ancestor", ancestorListener );
}
if( SystemInfo.isMacFullWindowContentSupported )
macFullWindowContentListener = FullWindowContentSupport.macInstallListeners( root );
macInstallWindowBackgroundListener( root );
}
@Override
protected void uninstallListeners( JRootPane root ) {
super.uninstallListeners( root );
if( SystemInfo.isJava_9_orLater ) {
if( componentListener != null ) {
Window window = SwingUtilities.windowForComponent( root );
if( window != null )
window.removeComponentListener( componentListener );
componentListener = null;
}
root.removePropertyChangeListener( "ancestor", ancestorListener );
ancestorListener = null;
if( SystemInfo.isMacFullWindowContentSupported ) {
FullWindowContentSupport.macUninstallListeners( root, macFullWindowContentListener );
macFullWindowContentListener = null;
}
macUninstallWindowBackgroundListener( root );
}
/** @since 1.1.2 */
@@ -302,19 +269,141 @@ public class FlatRootPaneUI
// layer title pane under frame content layer to allow placing menu bar over title pane
protected final static Integer TITLE_PANE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 1;
private final static Integer TITLE_PANE_MOUSE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 2;
private final static Integer WINDOW_TOP_BORDER_LAYER = Integer.MAX_VALUE;
// for fullWindowContent mode, layer title pane over frame content layer to allow placing title bar buttons over content
/** @since 3.4 */
protected final static Integer TITLE_PANE_FULL_WINDOW_CONTENT_LAYER = JLayeredPane.FRAME_CONTENT_LAYER + 1;
private Integer getLayerForTitlePane() {
return isFullWindowContent( rootPane ) ? TITLE_PANE_FULL_WINDOW_CONTENT_LAYER : TITLE_PANE_LAYER;
}
protected void setTitlePane( FlatTitlePane newTitlePane ) {
JLayeredPane layeredPane = rootPane.getLayeredPane();
if( titlePane != null )
if( titlePane != null ) {
layeredPane.remove( titlePane );
layeredPane.remove( titlePane.mouseLayer );
if( titlePane.windowTopBorderLayer != null )
layeredPane.remove( titlePane.windowTopBorderLayer );
}
if( newTitlePane != null )
layeredPane.add( newTitlePane, TITLE_PANE_LAYER );
if( newTitlePane != null ) {
layeredPane.add( newTitlePane, getLayerForTitlePane() );
layeredPane.add( newTitlePane.mouseLayer, TITLE_PANE_MOUSE_LAYER );
if( newTitlePane.windowTopBorderLayer != null && newTitlePane.isWindowTopBorderNeeded() && isFullWindowContent( rootPane ) )
layeredPane.add( newTitlePane.windowTopBorderLayer, WINDOW_TOP_BORDER_LAYER );
}
titlePane = newTitlePane;
}
private void macInstallFullWindowContentSupport() {
if( !SystemInfo.isMacOS )
return;
// set window buttons spacing
if( isMacButtonsSpacingSupported() && rootPane.isDisplayable() ) {
int buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT;
String value = (String) rootPane.getClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING );
if( value != null ) {
switch( value ) {
case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_MEDIUM:
buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_MEDIUM;
break;
case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_LARGE:
buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_LARGE;
break;
}
}
FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow( rootPane ), buttonsSpacing );
}
// update buttons bounds client property
FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty( rootPane );
}
private void macUninstallFullWindowContentSupport() {
if( !SystemInfo.isMacOS )
return;
// do not uninstall when switching to another FlatLaf theme
if( UIManager.getLookAndFeel() instanceof FlatLaf )
return;
// reset window buttons spacing
if( isMacButtonsSpacingSupported() )
FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow( rootPane ), FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT );
// remove buttons bounds client property
FullWindowContentSupport.macUninstallFullWindowContentButtonsBoundsProperty( rootPane );
}
private boolean isMacButtonsSpacingSupported() {
return SystemInfo.isMacOS && SystemInfo.isJava_17_orLater && FlatNativeMacLibrary.isLoaded();
}
private void macInstallWindowBackgroundListener( JRootPane c ) {
if( !SystemInfo.isMacOS )
return;
Window window = getParentWindow( c );
if( window != null && macWindowBackgroundListener == null ) {
macWindowBackgroundListener = e -> macClearBackgroundForTranslucentWindow( c );
window.addPropertyChangeListener( "background", macWindowBackgroundListener );
}
}
private void macUninstallWindowBackgroundListener( JRootPane c ) {
if( !SystemInfo.isMacOS )
return;
Window window = getParentWindow( c );
if( window != null && macWindowBackgroundListener != null ) {
window.removePropertyChangeListener( "background", macWindowBackgroundListener );
macWindowBackgroundListener = null;
}
}
/**
* When setting window background to translucent color (alpha < 255),
* Swing paints that window translucent on Windows and Linux, but not on macOS.
* The reason for this is that FlatLaf sets the background color of the root pane,
* and Swing behaves a bit differently on macOS than on other platforms in that case.
* Other L&Fs do not set root pane background, which is {@code null} by default.
* <p>
* To fix this problem, set the root pane background to {@code null}
* if windows uses a translucent background.
*/
private void macClearBackgroundForTranslucentWindow( JRootPane c ) {
if( !SystemInfo.isMacOS )
return;
Window window = getParentWindow( c );
if( window != null ) {
Color windowBackground = window.getBackground();
if( windowBackground != null &&
windowBackground.getAlpha() < 255 &&
c.getBackground() instanceof UIResource )
{
// clear root pane background
c.setBackground( null );
}
}
}
private Window getParentWindow( JRootPane c ) {
// not using SwingUtilities.windowForComponent() or SwingUtilities.getWindowAncestor()
// here because root panes may be nested and used anywhere (e.g. in JInternalFrame)
// but we're only interested in the "root" root pane, which is a direct child of the window
Container parent = c.getParent();
return (parent instanceof Window) ? (Window) parent : null;
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
@@ -359,6 +448,28 @@ public class FlatRootPaneUI
titlePane.titleBarColorsChanged();
break;
case FlatClientProperties.FULL_WINDOW_CONTENT:
if( titlePane != null ) {
rootPane.getLayeredPane().setLayer( titlePane, getLayerForTitlePane() );
if( titlePane.windowTopBorderLayer != null ) {
JLayeredPane layeredPane = rootPane.getLayeredPane();
if( titlePane.isWindowTopBorderNeeded() && isFullWindowContent( rootPane ) )
layeredPane.add( titlePane.windowTopBorderLayer, WINDOW_TOP_BORDER_LAYER );
else
layeredPane.remove( titlePane.windowTopBorderLayer );
}
titlePane.updateIcon();
titlePane.updateVisibility();
titlePane.updateFullWindowContentButtonsBoundsProperty();
}
FullWindowContentSupport.revalidatePlaceholders( rootPane );
rootPane.revalidate();
break;
case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS:
FullWindowContentSupport.revalidatePlaceholders( rootPane );
break;
case FlatClientProperties.GLASS_PANE_FULL_HEIGHT:
rootPane.revalidate();
break;
@@ -367,14 +478,43 @@ public class FlatRootPaneUI
if( rootPane.isDisplayable() )
throw new IllegalComponentStateException( "The client property 'Window.style' must be set before the window becomes displayable." );
break;
case "ancestor":
if( e.getNewValue() instanceof Window )
macClearBackgroundForTranslucentWindow( rootPane );
macUninstallWindowBackgroundListener( rootPane );
macInstallWindowBackgroundListener( rootPane );
// FlatNativeMacLibrary.setWindowButtonsSpacing() and
// FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty()
// require a native window, but setting the client properties
// "apple.awt.fullWindowContent" or FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING
// is usually done before the native window is created
// --> try again when native window is created
if( e.getNewValue() instanceof Window )
macInstallFullWindowContentSupport();
break;
case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING:
macInstallFullWindowContentSupport();
break;
case "apple.awt.fullWindowContent":
if( SystemInfo.isMacFullWindowContentSupported )
FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty( rootPane );
break;
}
}
/** @since 3.4 */
protected static boolean isFullWindowContent( JRootPane rootPane ) {
return FlatClientProperties.clientPropertyBoolean( rootPane, FlatClientProperties.FULL_WINDOW_CONTENT, false );
}
protected static boolean isMenuBarEmbedded( JRootPane rootPane ) {
RootPaneUI ui = rootPane.getUI();
return ui instanceof FlatRootPaneUI &&
((FlatRootPaneUI)ui).titlePane != null &&
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
FlatTitlePane titlePane = getTitlePane( rootPane );
return titlePane != null && titlePane.isMenuBarEmbedded();
}
/** @since 2.4 */
@@ -410,23 +550,21 @@ public class FlatRootPaneUI
private Dimension computeLayoutSize( Container parent, Function<Component, Dimension> getSizeFunc ) {
JRootPane rootPane = (JRootPane) parent;
Dimension titlePaneSize = (titlePane != null)
? getSizeFunc.apply( titlePane )
: new Dimension();
Dimension contentSize = (rootPane.getContentPane() != null)
? getSizeFunc.apply( rootPane.getContentPane() )
: rootPane.getSize();
: rootPane.getSize(); // same as in JRootPane.RootLayout.preferredLayoutSize()
int width = contentSize.width; // title pane width is not considered here
int height = titlePaneSize.height + contentSize.height;
int height = contentSize.height;
if( titlePane != null && !isFullWindowContent( rootPane ) )
height += getSizeFunc.apply( titlePane ).height;
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
JMenuBar menuBar = rootPane.getJMenuBar();
Dimension menuBarSize = (menuBar != null && menuBar.isVisible())
? getSizeFunc.apply( menuBar )
: new Dimension();
width = Math.max( width, menuBarSize.width );
height += menuBarSize.height;
if( menuBar != null && menuBar.isVisible() ) {
Dimension menuBarSize = getSizeFunc.apply( menuBar );
width = Math.max( width, menuBarSize.width );
height += menuBarSize.height;
}
}
Insets insets = rootPane.getInsets();
@@ -451,12 +589,29 @@ public class FlatRootPaneUI
if( rootPane.getLayeredPane() != null )
rootPane.getLayeredPane().setBounds( x, y, width, height );
// title pane
// title pane (is a child of layered pane)
int nextY = 0;
if( titlePane != null ) {
int prefHeight = !isFullScreen ? titlePane.getPreferredSize().height : 0;
titlePane.setBounds( 0, 0, width, prefHeight );
nextY += prefHeight;
boolean isFullWindowContent = isFullWindowContent( rootPane );
if( isFullWindowContent && !UIManager.getBoolean( FlatTitlePane.KEY_DEBUG_SHOW_RECTANGLES ) ) {
// place title bar into top-right corner
int tw = Math.min( titlePane.getPreferredSize().width, width );
int tx = titlePane.getComponentOrientation().isLeftToRight() ? width - tw : 0;
titlePane.setBounds( tx, 0, tw, prefHeight );
} else
titlePane.setBounds( 0, 0, width, prefHeight );
titlePane.mouseLayer.setBounds( 0, 0, width, prefHeight );
if( titlePane.windowTopBorderLayer != null ) {
boolean show = isFullWindowContent && !titlePane.isWindowMaximized() && !isFullScreen;
if( show )
titlePane.windowTopBorderLayer.setBounds( 0, 0, width, 1 );
titlePane.windowTopBorderLayer.setVisible( show );
}
if( !isFullWindowContent )
nextY += prefHeight;
}
// glass pane
@@ -467,7 +622,7 @@ public class FlatRootPaneUI
rootPane.getGlassPane().setBounds( x, y + offset, width, height - offset );
}
// menu bar
// menu bar (is a child of layered pane)
JMenuBar menuBar = rootPane.getJMenuBar();
if( menuBar != null && menuBar.isVisible() ) {
boolean embedded = !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded();
@@ -475,13 +630,23 @@ public class FlatRootPaneUI
titlePane.validate();
menuBar.setBounds( titlePane.getMenuBarBounds() );
} else {
int mx = 0;
int mw = width;
if( titlePane != null && isFullWindowContent( rootPane ) ) {
// make menu bar width smaller to avoid that it overlaps title bar buttons
int tw = Math.min( titlePane.getPreferredSize().width, width );
mw -= tw;
if( !titlePane.getComponentOrientation().isLeftToRight() )
mx = tw;
}
Dimension prefSize = menuBar.getPreferredSize();
menuBar.setBounds( 0, nextY, width, prefSize.height );
menuBar.setBounds( mx, nextY, mw, prefSize.height );
nextY += prefSize.height;
}
}
// content pane
// content pane (is a child of layered pane)
Container contentPane = rootPane.getContentPane();
if( contentPane != null )
contentPane.setBounds( 0, nextY, width, Math.max( height - nextY, 0 ) );

View File

@@ -84,7 +84,7 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatSplitPaneUI
extends BasicSplitPaneUI
implements StyleableUI
implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
{
@Styleable protected String arrowType;
/** @since 3.3 */ @Styleable protected Color draggingColor;
@@ -227,6 +227,15 @@ public class FlatSplitPaneUI
((FlatSplitPaneDivider)divider).paintStyle( g, x, y, width, height );
}
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
/** @since 3.4 */
@Override
public Boolean isTitleBarCaptionAt( int x, int y ) {
// necessary because BasicSplitPaneDivider adds some mouse listeners for dragging divider
return null; // check children
}
//---- class FlatSplitPaneDivider -----------------------------------------
protected class FlatSplitPaneDivider
@@ -292,6 +301,10 @@ public class FlatSplitPaneUI
// necessary to show/hide one-touch buttons on expand/collapse
doLayout();
break;
case FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE:
revalidate();
break;
}
}

View File

@@ -182,7 +182,7 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatTabbedPaneUI
extends BasicTabbedPaneUI
implements StyleableUI
implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
{
// tab type
/** @since 2 */ protected static final int TAB_TYPE_UNDERLINED = 0;
@@ -2300,6 +2300,17 @@ debug*/
return (rects[last].y + rects[last].height) - rects[0].y;
}
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
/** @since 3.4 */
@Override
public Boolean isTitleBarCaptionAt( int x, int y ) {
if( tabForCoordinate( tabPane, x, y ) >= 0 )
return false;
return null; // check children
}
//---- class TabCloseButton -----------------------------------------------
private static class TabCloseButton
@@ -3756,9 +3767,20 @@ debug*/
boolean hideDisabledScrollButtons = (scrollButtonsPolicy == AS_NEEDED_SINGLE && scrollButtonsPlacement == BOTH);
boolean trailingScrollButtons = (scrollButtonsPlacement == TRAILING);
// for right-to-left always use "more tabs" button for horizontal scrolling
// For right-to-left, always use "more tabs" button for horizontal scrolling
// because methods scrollForward() and scrollBackward() in class
// BasicTabbedPaneUI.ScrollableTabSupport do not work for right-to-left
// BasicTabbedPaneUI.ScrollableTabSupport do not work for right-to-left.
//
// One problem is that BasicTabbedPaneUI.getClosestTab(), which is used
// to compute leadingTabIndex, does not work for right-to-left because is uses "binary" search
// on rects[] to find tab, but rects[] is ordered in reverse order for right-to-left.
// So leadingTabIndex is either zero or tabCount.
// Therefore increasing/decreasing leadingTabIndex in scrollForward()
// and scrollBackward() does not work as expected.
// Also backward/forward scroll buttons are not correctly enabled/disabled.
//
// Fixing this would require replacing nearly whole functionality of class
// BasicTabbedPaneUI.ScrollableTabSupport, which is not possible because it is private.
boolean leftToRight = isLeftToRight();
if( !leftToRight && isHorizontalTabPlacement( tabPane.getTabPlacement() ) ) {
useMoreTabsButton = true;
@@ -3840,6 +3862,8 @@ debug*/
w -= buttonWidth;
moreTabsButtonVisible = true;
}
// layout scroll buttons
if( useScrollButtons ) {
// the tabViewport view size is set in
// BasicTabbedPaneUI.TabbedPaneScrollLayout.calculateTabRects(),
@@ -3847,30 +3871,28 @@ debug*/
Point viewPosition = tabViewport.getViewPosition();
Dimension viewSize = tabViewport.getViewSize();
// layout forward button on trailing side
if( !hideDisabledScrollButtons || viewSize.width - viewPosition.x > w ) {
int buttonWidth = forwardButton.getPreferredSize().width;
forwardButton.setBounds( x + w - buttonWidth, y, buttonWidth, h );
w -= buttonWidth;
forwardButtonVisible = true;
}
// layout backward button
if( !hideDisabledScrollButtons || viewPosition.x > 0 ) {
int buttonWidth = backwardButton.getPreferredSize().width;
if( trailingScrollButtons ) {
// on trailing side
backwardButton.setBounds( leftToRight ? (x + w - buttonWidth) : x, y, buttonWidth, h );
x += leftToRight ? 0 : buttonWidth;
backwardButton.setBounds( x + w - buttonWidth, y, buttonWidth, h );
} else {
// on leading side
backwardButton.setBounds( leftToRight ? x : (x + w - buttonWidth), y, buttonWidth, h );
x += leftToRight ? buttonWidth : 0;
backwardButton.setBounds( x, y, buttonWidth, h );
x += buttonWidth;
}
w -= buttonWidth;
backwardButtonVisible = true;
}
// layout forward button on trailing side
if( !hideDisabledScrollButtons || viewSize.width - viewPosition.x > w ) {
int buttonWidth = forwardButton.getPreferredSize().width;
forwardButton.setBounds( leftToRight ? (x + w - buttonWidth) : x, y, buttonWidth, h );
x += leftToRight ? 0 : buttonWidth;
w -= buttonWidth;
forwardButtonVisible = true;
}
}
}
@@ -3916,6 +3938,8 @@ debug*/
h -= buttonHeight;
moreTabsButtonVisible = true;
}
// layout scroll buttons
if( useScrollButtons ) {
// the tabViewport view size is set in
// BasicTabbedPaneUI.TabbedPaneScrollLayout.calculateTabRects(),
@@ -3923,6 +3947,14 @@ debug*/
Point viewPosition = tabViewport.getViewPosition();
Dimension viewSize = tabViewport.getViewSize();
// layout forward button on bottom side
if( !hideDisabledScrollButtons || viewSize.height - viewPosition.y > h ) {
int buttonHeight = forwardButton.getPreferredSize().height;
forwardButton.setBounds( x, y + h - buttonHeight, w, buttonHeight );
h -= buttonHeight;
forwardButtonVisible = true;
}
// layout backward button
if( !hideDisabledScrollButtons || viewPosition.y > 0 ) {
int buttonHeight = backwardButton.getPreferredSize().height;
@@ -3937,14 +3969,6 @@ debug*/
h -= buttonHeight;
backwardButtonVisible = true;
}
// layout forward button on bottom side
if( !hideDisabledScrollButtons || viewSize.height - viewPosition.y > h ) {
int buttonHeight = forwardButton.getPreferredSize().height;
forwardButton.setBounds( x, y + h - buttonHeight, w, buttonHeight );
h -= buttonHeight;
forwardButtonVisible = true;
}
}
}
@@ -3985,10 +4009,8 @@ debug*/
//---- class RunWithOriginalLayoutManagerDelegateAction -------------------
private static class RunWithOriginalLayoutManagerDelegateAction
implements Action
extends FlatUIAction
{
private final Action delegate;
static void install( ActionMap map, String key ) {
Action oldAction = map.get( key );
if( oldAction == null || oldAction instanceof RunWithOriginalLayoutManagerDelegateAction )
@@ -3998,24 +4020,9 @@ debug*/
}
private RunWithOriginalLayoutManagerDelegateAction( Action delegate ) {
this.delegate = delegate;
super( delegate );
}
@Override
public Object getValue( String key ) {
return delegate.getValue( key );
}
@Override
public boolean isEnabled() {
return delegate.isEnabled();
}
@Override public void putValue( String key, Object value ) {}
@Override public void setEnabled( boolean b ) {}
@Override public void addPropertyChangeListener( PropertyChangeListener listener ) {}
@Override public void removePropertyChangeListener( PropertyChangeListener listener ) {}
@Override
public void actionPerformed( ActionEvent e ) {
JTabbedPane tabbedPane = (JTabbedPane) e.getSource();

View File

@@ -24,15 +24,19 @@ import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.ActionEvent;
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.Action;
import javax.swing.ActionMap;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JViewport;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
@@ -89,6 +93,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Table.selectionInactiveBackground Color
* @uiDefault Table.selectionInactiveForeground Color
* @uiDefault Table.paintOutsideAlternateRows boolean
* @uiDefault Table.editorSelectAllOnStartEditing boolean
*
* <!-- FlatTableCellBorder -->
*
@@ -284,6 +289,18 @@ public class FlatTableUI
};
}
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
if( UIManager.getBoolean( "Table.editorSelectAllOnStartEditing" ) ) {
// get shared action map, used for all tables
ActionMap map = SwingUtilities.getUIActionMap( table );
if( map != null )
StartEditingAction.install( map, "startEditing" );
}
}
/** @since 2 */
protected void installStyle() {
try {
@@ -579,4 +596,36 @@ public class FlatTableUI
selected = (value != null && (Boolean) value);
}
}
//---- class StartEditingAction -------------------------------------------
private static class StartEditingAction
extends FlatUIAction
{
static void install( ActionMap map, String key ) {
Action oldAction = map.get( key );
if( oldAction == null || oldAction instanceof StartEditingAction )
return; // not found or already installed
map.put( key, new StartEditingAction( oldAction ) );
}
private StartEditingAction( Action delegate ) {
super( delegate );
}
@Override
public void actionPerformed( ActionEvent e ) {
JTable table = (JTable) e.getSource();
Component oldEditorComp = table.getEditorComponent();
delegate.actionPerformed( e );
// select all text in editor if editing starts with F2 key
Component editorComp = table.getEditorComponent();
if( oldEditorComp == null && editorComp instanceof JTextField )
((JTextField)editorComp).selectAll();
}
}
}

View File

@@ -36,6 +36,7 @@ import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
@@ -46,9 +47,9 @@ import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import javax.accessibility.AccessibleContext;
import javax.swing.BorderFactory;
import javax.swing.Box;
@@ -57,7 +58,6 @@ import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
@@ -66,6 +66,7 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.FlatNativeWindowBorder.WindowTopBorder;
@@ -98,7 +99,6 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.showIconBesideTitle boolean
* @uiDefault TitlePane.menuBarTitleGap int
* @uiDefault TitlePane.menuBarTitleMinimumGap int
* @uiDefault TitlePane.menuBarResizeHeight int
* @uiDefault TitlePane.closeIcon Icon
* @uiDefault TitlePane.iconifyIcon Icon
* @uiDefault TitlePane.maximizeIcon Icon
@@ -109,7 +109,8 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatTitlePane
extends JComponent
{
private static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles";
static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles";
private static final boolean isWindows_10 = SystemInfo.isWindows_10_orLater && !SystemInfo.isWindows_11_orLater;
/** @since 2.5 */ protected final Font titleFont;
protected final Color activeBackground;
@@ -131,7 +132,6 @@ public class FlatTitlePane
/** @since 2.4 */ protected final boolean showIconBesideTitle;
protected final int menuBarTitleGap;
/** @since 2.4 */ protected final int menuBarTitleMinimumGap;
/** @since 2.4 */ protected final int menuBarResizeHeight;
protected final JRootPane rootPane;
protected final String windowStyle;
@@ -150,6 +150,33 @@ public class FlatTitlePane
private final Handler handler;
/**
* This panel handles mouse events if FlatLaf window decorations are used
* without native window border. E.g. on Linux.
* <p>
* This panel usually has same bounds as the title pane,
* except if fullWindowContent mode is enabled.
* <p>
* This panel is not a child of the title pane.
* Instead it is added by FlatRootPaneUI to the layered pane at a layer
* under the title pane and under the frame content.
* The separation is necessary for fullWindowContent mode, where the title pane
* is layered over the frame content (for title pane buttons), but the mousePanel
* needs to be layered under the frame content so that components on content pane
* can receive mouse events when located in title area.
*/
final JPanel mouseLayer;
/**
* This panel paint a border at the top of the window in fullWindowContent mode,
* if FlatLaf window decorations are enabled.
* Only used on Windows 10.
* <p>
* This panel is not a child of the title pane.
* Instead it is added by FlatRootPaneUI to the layered pane at a layer over all other layers.
*/
final JPanel windowTopBorderLayer;
public FlatTitlePane( JRootPane rootPane ) {
this.rootPane = rootPane;
@@ -178,7 +205,6 @@ public class FlatTitlePane
showIconBesideTitle = FlatUIUtils.getSubUIBoolean( "TitlePane.showIconBesideTitle", windowStyle, false );
menuBarTitleGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleGap", windowStyle, 40 );
menuBarTitleMinimumGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleMinimumGap", windowStyle, 12 );
menuBarResizeHeight = FlatUIUtils.getSubUIInt( "TitlePane.menuBarResizeHeight", windowStyle, 4 );
handler = createHandler();
@@ -187,11 +213,18 @@ public class FlatTitlePane
addSubComponents();
activeChanged( true );
addMouseListener( handler );
addMouseMotionListener( handler );
mouseLayer = new JPanel();
mouseLayer.setOpaque( false );
mouseLayer.addMouseListener( handler );
mouseLayer.addMouseMotionListener( handler );
// necessary for closing window with double-click on icon
iconLabel.addMouseListener( handler );
if( isWindows_10 && FlatNativeWindowBorder.isSupported() ) {
windowTopBorderLayer = new JPanel();
windowTopBorderLayer.setVisible( false );
windowTopBorderLayer.setOpaque( false );
windowTopBorderLayer.setBorder( FlatUIUtils.nonUIResource( FlatNativeWindowBorder.WindowTopBorder.getInstance() ) );
} else
windowTopBorderLayer = null;
applyComponentOrientation( rootPane.getComponentOrientation() );
}
@@ -234,6 +267,11 @@ public class FlatTitlePane
setLayout( new BorderLayout() {
@Override
public void layoutContainer( Container target ) {
if( isFullWindowContent() ) {
super.layoutContainer( target );
return;
}
// compute available bounds
Insets insets = target.getInsets();
int x = insets.left;
@@ -247,7 +285,7 @@ public class FlatTitlePane
int titleWidth = w - leftWidth - buttonsWidth;
int minTitleWidth = UIScale.scale( titleMinimumWidth );
// increase minimum width if icon is show besides the title
// increase minimum width if icon is shown besides the title
Icon icon = titleLabel.getIcon();
if( icon != null ) {
Insets iconInsets = iconLabel.getInsets();
@@ -295,6 +333,9 @@ public class FlatTitlePane
horizontalGlue.getWidth(), titleLabel.getHeight() );
}
}
// clear hit-test cache
lastCaptionHitTestTime = 0;
}
} );
@@ -338,6 +379,13 @@ public class FlatTitlePane
buttonPanel.add( restoreButton );
}
buttonPanel.add( closeButton );
ComponentListener l = new ComponentAdapter() {
@Override public void componentResized( ComponentEvent e ) { updateFullWindowContentButtonsBoundsProperty(); }
@Override public void componentMoved( ComponentEvent e ) { updateFullWindowContentButtonsBoundsProperty(); }
};
buttonPanel.addComponentListener( l );
addComponentListener( l );
}
protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) {
@@ -417,7 +465,9 @@ public class FlatTitlePane
/** @since 3 */
protected void updateVisibility() {
titleLabel.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_TITLE, true ) );
boolean isFullWindowContent = isFullWindowContent();
leftPanel.setVisible( !isFullWindowContent );
titleLabel.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_TITLE, true ) && !isFullWindowContent );
closeButton.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_CLOSE, true ) );
if( window instanceof Frame ) {
@@ -443,7 +493,7 @@ public class FlatTitlePane
// get window images
List<Image> images = null;
if( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_ICON, defaultShowIcon ) ) {
if( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_ICON, defaultShowIcon ) && !isFullWindowContent() ) {
images = window.getIconImages();
if( images.isEmpty() ) {
// search in owners
@@ -468,6 +518,13 @@ public class FlatTitlePane
updateNativeTitleBarHeightAndHitTestSpotsLater();
}
void updateFullWindowContentButtonsBoundsProperty() {
Rectangle bounds = isFullWindowContent()
? new Rectangle( SwingUtilities.convertPoint( buttonPanel, 0, 0, rootPane ), buttonPanel.getSize() )
: null;
rootPane.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS, bounds );
}
@Override
public void addNotify() {
super.addNotify();
@@ -522,6 +579,11 @@ public class FlatTitlePane
window.removeComponentListener( handler );
}
/** @since 3.4 */
protected boolean isFullWindowContent() {
return FlatRootPaneUI.isFullWindowContent( rootPane );
}
/**
* Returns whether this title pane currently has a visible and embedded menubar.
*/
@@ -533,6 +595,9 @@ public class FlatTitlePane
* Returns whether the menubar should be embedded into the title pane.
*/
protected boolean isMenuBarEmbedded() {
if( isFullWindowContent() )
return false;
// not storing value of "TitlePane.menuBarEmbedded" in class to allow changing at runtime
return FlatUIUtils.getBoolean( rootPane,
FlatSystemProperties.MENUBAR_EMBEDDED,
@@ -620,21 +685,45 @@ public class FlatTitlePane
return;
if( debugTitleBarHeight > 0 ) {
// title bar height is measured from window top edge
int y = SwingUtilities.convertPoint( window, 0, debugTitleBarHeight, this ).y;
g.setColor( Color.green );
g.drawLine( 0, debugTitleBarHeight, getWidth(), debugTitleBarHeight );
g.drawLine( 0, y, getWidth(), y );
}
if( debugHitTestSpots != null ) {
for( Rectangle r : debugHitTestSpots )
paintRect( g, Color.red, r );
}
paintRect( g, Color.cyan, debugCloseButtonBounds );
paintRect( g, Color.blue, debugAppIconBounds );
paintRect( g, Color.blue, debugMinimizeButtonBounds );
paintRect( g, Color.magenta, debugMaximizeButtonBounds );
paintRect( g, Color.cyan, debugCloseButtonBounds );
g.setColor( Color.red );
debugPaintComponentWithMouseListener( g, Color.red, rootPane.getLayeredPane(), 0, 0 );
debugPaintRect( g, Color.blue, debugAppIconBounds );
debugPaintRect( g, Color.blue, debugMinimizeButtonBounds );
debugPaintRect( g, Color.magenta, debugMaximizeButtonBounds );
debugPaintRect( g, Color.cyan, debugCloseButtonBounds );
}
private void paintRect( Graphics g, Color color, Rectangle r ) {
private void debugPaintComponentWithMouseListener( Graphics g, Color color, Component c, int x, int y ) {
if( !c.isDisplayable() || !c.isVisible() || c == mouseLayer ||
c == iconifyButton || c == maximizeButton || c == restoreButton || c == closeButton )
return;
if( c.getMouseListeners().length > 0 ||
c.getMouseMotionListeners().length > 0 ||
c.getMouseWheelListeners().length > 0 )
{
g.drawRect( x, y, c.getWidth(), c.getHeight() );
return;
}
if( c instanceof Container ) {
Rectangle titlePaneBoundsOnWindow = SwingUtilities.convertRectangle( this, new Rectangle( getSize() ), window );
for( Component child : ((Container)c).getComponents() ) {
Rectangle compBoundsOnWindow = SwingUtilities.convertRectangle( c, new Rectangle( c.getSize() ), window );
if( compBoundsOnWindow.intersects( titlePaneBoundsOnWindow ) )
debugPaintComponentWithMouseListener( g, color, child, x + child.getX(), y + child.getY() );
}
}
}
private void debugPaintRect( Graphics g, Color color, Rectangle r ) {
if( r == null )
return;
@@ -645,6 +734,9 @@ public class FlatTitlePane
@Override
protected void paintComponent( Graphics g ) {
if( isFullWindowContent() )
return;
// not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime
g.setColor( (UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
clientPropertyColor( rootPane, TITLE_BAR_BACKGROUND, null ) == null)
@@ -846,6 +938,10 @@ public class FlatTitlePane
return window != null && FlatNativeWindowBorder.hasCustomDecoration( window );
}
boolean isWindowTopBorderNeeded() {
return isWindows_10 && hasNativeCustomDecoration();
}
// used to invoke updateNativeTitleBarHeightAndHitTestSpots() only once from latest invokeLater()
private int laterCounter;
@@ -866,11 +962,14 @@ public class FlatTitlePane
return;
int titleBarHeight = getHeight();
// title bar height must be measured from window top edge
// (when window is maximized, window y location is e.g. -11 and window top inset is 11)
for( Component c = this; c != window && c != null; c = c.getParent() )
titleBarHeight += c.getY();
// slightly reduce height so that component receives mouseExit events
if( titleBarHeight > 0 )
titleBarHeight--;
List<Rectangle> hitTestSpots = new ArrayList<>();
Rectangle appIconBounds = null;
if( !showIconBesideTitle && iconLabel.isVisible() ) {
@@ -928,71 +1027,17 @@ public class FlatTitlePane
}
}
Rectangle r = getNativeHitTestSpot( buttonPanel );
if( r != null )
hitTestSpots.add( r );
JMenuBar menuBar = rootPane.getJMenuBar();
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
r = getNativeHitTestSpot( menuBar );
if( r != null ) {
// if frame is resizable and not maximized, make menu bar hit test spot smaller at top
// to have a small area above the menu bar to resize the window
if( window instanceof Frame && ((Frame)window).isResizable() && !isWindowMaximized() ) {
// limit to 8, because Windows does not use a larger height
int resizeHeight = UIScale.scale( Math.min( menuBarResizeHeight, 8 ) );
r.y += resizeHeight;
r.height -= resizeHeight;
}
int count = menuBar.getComponentCount();
for( int i = count - 1; i >= 0; i-- ) {
Component c = menuBar.getComponent( i );
if( c instanceof Box.Filler ||
(c instanceof JComponent && clientPropertyBoolean( (JComponent) c, COMPONENT_TITLE_BAR_CAPTION, false ) ) )
{
// If menu bar is embedded and contains a horizontal glue or caption component,
// then split the hit test spot so that
// the glue/caption component area can be used to move the window.
Point glueLocation = SwingUtilities.convertPoint( c, 0, 0, window );
int x2 = glueLocation.x + c.getWidth();
Rectangle r2;
if( getComponentOrientation().isLeftToRight() ) {
r2 = new Rectangle( x2, r.y, (r.x + r.width) - x2, r.height );
r.width = glueLocation.x - r.x;
} else {
r2 = new Rectangle( r.x, r.y, glueLocation.x - r.x, r.height );
r.width = (r.x + r.width) - x2;
r.x = x2;
}
if( r2.width > 0 )
hitTestSpots.add( r2 );
}
}
hitTestSpots.add( r );
}
}
// allow internal frames in layered pane to be moved/resized when placed over title bar
for( Component c : rootPane.getLayeredPane().getComponents() ) {
r = (c instanceof JInternalFrame) ? getNativeHitTestSpot( (JInternalFrame) c ) : null;
if( r != null )
hitTestSpots.add( r );
}
Rectangle minimizeButtonBounds = boundsInWindow( iconifyButton );
Rectangle maximizeButtonBounds = boundsInWindow( maximizeButton.isVisible() ? maximizeButton : restoreButton );
Rectangle closeButtonBounds = boundsInWindow( closeButton );
// clear hit-test cache
lastCaptionHitTestTime = 0;
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight,
hitTestSpots, appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
this::captionHitTest, appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
debugTitleBarHeight = titleBarHeight;
debugHitTestSpots = hitTestSpots;
debugAppIconBounds = appIconBounds;
debugMinimizeButtonBounds = minimizeButtonBounds;
debugMaximizeButtonBounds = maximizeButtonBounds;
@@ -1007,18 +1052,101 @@ public class FlatTitlePane
: null;
}
protected Rectangle getNativeHitTestSpot( JComponent c ) {
Dimension size = c.getSize();
if( size.width <= 0 || size.height <= 0 )
return null;
/**
* Returns whether there is a component at the given location, that processes
* mouse events. E.g. buttons, menus, etc.
* <p>
* Note:
* <ul>
* <li>This method is invoked often when mouse is moved over title bar
* and should therefore return quickly.
* <li>This method is invoked on 'AWT-Windows' thread (not 'AWT-EventQueue' thread)
* while processing Windows messages.
* </ul>
*/
private boolean captionHitTest( Point pt ) {
// Windows invokes this method every ~200ms, even if the mouse has not moved
long time = System.currentTimeMillis();
if( pt.x == lastCaptionHitTestX && pt.y == lastCaptionHitTestY && time < lastCaptionHitTestTime + 300 ) {
lastCaptionHitTestTime = time;
return lastCaptionHitTestResult;
}
Point location = SwingUtilities.convertPoint( c, 0, 0, window );
Rectangle r = new Rectangle( location, size );
return r;
// convert pt from window coordinates to layeredPane coordinates
Component layeredPane = rootPane.getLayeredPane();
int x = pt.x;
int y = pt.y;
for( Component c = layeredPane; c != window && c != null; c = c.getParent() ) {
x -= c.getX();
y -= c.getY();
}
lastCaptionHitTestX = pt.x;
lastCaptionHitTestY = pt.y;
lastCaptionHitTestTime = time;
lastCaptionHitTestResult = isTitleBarCaptionAt( layeredPane, x, y );
return lastCaptionHitTestResult;
}
private boolean isTitleBarCaptionAt( Component c, int x, int y ) {
if( !c.isDisplayable() || !c.isVisible() || !c.contains( x, y ) || c == mouseLayer )
return true; // continue checking with next component
if( c.isEnabled() &&
(c.getMouseListeners().length > 0 ||
c.getMouseMotionListeners().length > 0) )
{
if( !(c instanceof JComponent) )
return false; // assume that this is not a caption because the component has mouse listeners
// check client property boolean value
Object caption = ((JComponent)c).getClientProperty( COMPONENT_TITLE_BAR_CAPTION );
if( caption instanceof Boolean )
return (boolean) caption;
// if component is not fully layouted, do not invoke function
// because it is too dangerous that the function tries to layout the component,
// which could cause a dead lock
if( !c.isValid() )
return false; // assume that this is not a caption because the component has mouse listeners
if( caption instanceof Function ) {
// check client property function value
@SuppressWarnings( "unchecked" )
Function<Point, Boolean> hitTest = (Function<Point, Boolean>) caption;
Boolean result = hitTest.apply( new Point( x, y ) );
if( result != null )
return result;
} else {
// check component UI
ComponentUI ui = JavaCompatibility2.getUI( (JComponent) c );
if( !(ui instanceof TitleBarCaptionHitTest) )
return false; // assume that this is not a caption because the component has mouse listeners
Boolean result = ((TitleBarCaptionHitTest)ui).isTitleBarCaptionAt( x, y );
if( result != null )
return result;
}
// else continue checking children
}
// check children
if( c instanceof Container ) {
for( Component child : ((Container)c).getComponents() ) {
if( !isTitleBarCaptionAt( child, x - child.getX(), y - child.getY() ) )
return false;
}
}
return true;
}
private int lastCaptionHitTestX;
private int lastCaptionHitTestY;
private long lastCaptionHitTestTime;
private boolean lastCaptionHitTestResult;
private int debugTitleBarHeight;
private List<Rectangle> debugHitTestSpots;
private Rectangle debugAppIconBounds;
private Rectangle debugMinimizeButtonBounds;
private Rectangle debugMaximizeButtonBounds;
@@ -1041,7 +1169,7 @@ public class FlatTitlePane
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
insets.bottom += UIScale.scale( 1 );
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
if( isWindowTopBorderNeeded() && !isWindowMaximized() )
insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() );
return insets;
@@ -1060,7 +1188,7 @@ public class FlatTitlePane
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
}
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
if( isWindowTopBorderNeeded() && !isWindowMaximized() && !isFullWindowContent() )
WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
}
@@ -1116,7 +1244,7 @@ public class FlatTitlePane
}
}
// compute icon width and gap (if icon is show besides the title)
// compute icon width and gap (if icon is shown besides the title)
int iconTextGap = 0;
int iconWidthAndGap = 0;
if( icon != null ) {
@@ -1125,7 +1253,7 @@ public class FlatTitlePane
iconWidthAndGap = icon.getIconWidth() + iconTextGap;
}
// layout title and icon (if show besides the title)
// layout title and icon (if shown besides the title)
String clippedText = SwingUtilities.layoutCompoundLabel( label, fontMetrics, text, icon,
label.getVerticalAlignment(), label.getHorizontalAlignment(),
label.getVerticalTextPosition(), label.getHorizontalTextPosition(),
@@ -1224,7 +1352,7 @@ public class FlatTitlePane
activeChanged( true );
updateNativeTitleBarHeightAndHitTestSpots();
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() )
if( isWindowTopBorderNeeded() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder();
@@ -1235,7 +1363,7 @@ public class FlatTitlePane
activeChanged( false );
updateNativeTitleBarHeightAndHitTestSpots();
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() )
if( isWindowTopBorderNeeded() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder();
@@ -1275,7 +1403,7 @@ debug*/
}
if( e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton( e ) ) {
if( e.getSource() == iconLabel ) {
if( SwingUtilities.getDeepestComponentAt( FlatTitlePane.this, e.getX(), e.getY() ) == iconLabel ) {
// double-click on icon closes window
close();
} else if( !hasNativeCustomDecoration() ) {
@@ -1302,7 +1430,7 @@ debug*/
if( !SwingUtilities.isLeftMouseButton( e ) )
return;
dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window );
dragOffset = SwingUtilities.convertPoint( mouseLayer, e.getPoint(), window );
linuxNativeMove = false;
// on Linux, move or maximize/restore window
@@ -1415,4 +1543,27 @@ debug*/
@Override public void componentMoved( ComponentEvent e ) {}
@Override public void componentHidden( ComponentEvent e ) {}
}
//---- interface TitleBarCaptionHitTest -----------------------------------
/**
* For custom components use {@link FlatClientProperties#COMPONENT_TITLE_BAR_CAPTION}
* instead of this interface.
*
* @since 3.4
*/
public interface TitleBarCaptionHitTest {
/**
* Invoked for a component that is enabled and has mouse listeners,
* to check whether it processes mouse input at the given x/y location.
* Useful for components that do not use mouse input on whole component bounds.
* E.g. a tabbed pane with a few tabs has some empty space beside the tabs
* that can be used to move the window.
*
* @return {@code true} if the component is not interested in mouse input at the given location
* {@code false} if the component wants process mouse input at the given location
* {@code null} if the component children should be checked
*/
Boolean isTitleBarCaptionAt( int x, int y );
}
}

View File

@@ -82,7 +82,7 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatToolBarUI
extends BasicToolBarUI
implements StyleableUI
implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
{
/** @since 1.4 */ @Styleable protected boolean focusableButtons;
/** @since 2 */ @Styleable protected boolean arrowKeysOnlyNavigation;
@@ -453,6 +453,15 @@ public class FlatToolBarUI
: null;
}
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
/** @since 3.4 */
@Override
public Boolean isTitleBarCaptionAt( int x, int y ) {
// necessary because BasicToolBarUI adds some mouse listeners for dragging when toolbar is floatable
return null; // check children
}
//---- class FlatToolBarFocusTraversalPolicy ------------------------------
/**

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2024 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.beans.PropertyChangeListener;
import javax.swing.Action;
/**
* Base class for UI actions used in ActionMap.
* (similar to class sun.swing.UIAction)
*
* @author Karl Tauber
* @since 3.4
*/
public abstract class FlatUIAction
implements Action
{
protected final String name;
protected final Action delegate;
protected FlatUIAction( String name ) {
this.name = name;
this.delegate = null;
}
protected FlatUIAction( Action delegate ) {
this.name = null;
this.delegate = delegate;
}
@Override
public Object getValue( String key ) {
if( key == NAME && delegate == null )
return name;
return (delegate != null) ? delegate.getValue( key ) : null;
}
@Override
public boolean isEnabled() {
return (delegate != null) ? delegate.isEnabled() : true;
}
// do nothing in following methods because this class is immutable
@Override public void putValue( String key, Object value ) {}
@Override public void setEnabled( boolean b ) {}
@Override public void addPropertyChangeListener( PropertyChangeListener listener ) {}
@Override public void removePropertyChangeListener( PropertyChangeListener listener ) {}
}

View File

@@ -60,6 +60,7 @@ import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.tree.DefaultTreeCellEditor;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatIntelliJLaf;
import com.formdev.flatlaf.FlatLaf;
@@ -304,6 +305,10 @@ public class FlatUIUtils
if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c )
return true;
// check whether used as tree cell editor
if( parent instanceof DefaultTreeCellEditor.EditorContainer )
return true;
// check whether used as cell editor
// Table.editor is set in JTable.GenericEditor constructor
// Tree.cellEditor is set in sun.swing.FilePane.editFileName()

View File

@@ -29,8 +29,8 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.Timer;
@@ -89,7 +89,7 @@ class FlatWindowsNativeWindowBorder
return null;
// check whether native library was successfully loaded
if( !FlatNativeLibrary.isLoaded() )
if( !FlatNativeWindowsLibrary.isLoaded() )
return null;
// create new instance
@@ -159,7 +159,7 @@ class FlatWindowsNativeWindowBorder
}
@Override
public void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
public void updateTitleBarInfo( Window window, int titleBarHeight, Predicate<Point> captionHitTestCallback,
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
Rectangle closeButtonBounds )
{
@@ -168,7 +168,7 @@ class FlatWindowsNativeWindowBorder
return;
wndProc.titleBarHeight = titleBarHeight;
wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] );
wndProc.captionHitTestCallback = captionHitTestCallback;
wndProc.appIconBounds = cloneRectange( appIconBounds );
wndProc.minimizeButtonBounds = cloneRectange( minimizeButtonBounds );
wndProc.maximizeButtonBounds = cloneRectange( maximizeButtonBounds );
@@ -288,8 +288,8 @@ class FlatWindowsNativeWindowBorder
private final long hwnd;
// Swing coordinates/values may be scaled on a HiDPI screen
private int titleBarHeight;
private Rectangle[] hitTestSpots;
private int titleBarHeight; // measured from window top edge, which may be out-of-screen if maximized
private Predicate<Point> captionHitTestCallback;
private Rectangle appIconBounds;
private Rectangle minimizeButtonBounds;
private Rectangle maximizeButtonBounds;
@@ -340,50 +340,61 @@ class FlatWindowsNativeWindowBorder
private int onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
// scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen
Point pt = scaleDown( x, y );
int sx = pt.x;
int sy = pt.y;
// return HTSYSMENU if mouse is over application icon
// - left-click on HTSYSMENU area shows system menu
// - double-left-click sends WM_CLOSE
if( contains( appIconBounds, sx, sy ) )
if( contains( appIconBounds, pt ) )
return HTSYSMENU;
// return HTMINBUTTON if mouse is over minimize button
// - hovering mouse over HTMINBUTTON area shows tooltip on Windows 10/11
if( contains( minimizeButtonBounds, sx, sy ) )
if( contains( minimizeButtonBounds, pt ) )
return HTMINBUTTON;
// return HTMAXBUTTON if mouse is over maximize/restore button
// - hovering mouse over HTMAXBUTTON area shows tooltip on Windows 10
// - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
if( contains( maximizeButtonBounds, sx, sy ) )
if( contains( maximizeButtonBounds, pt ) )
return HTMAXBUTTON;
// return HTCLOSE if mouse is over close button
// - hovering mouse over HTCLOSE area shows tooltip on Windows 10/11
if( contains( closeButtonBounds, sx, sy ) )
if( contains( closeButtonBounds, pt ) )
return HTCLOSE;
boolean isOnTitleBar = (sy < titleBarHeight);
// return HTTOP if mouse is over top resize border
// - hovering mouse shows vertical resize cursor
// - left-click and drag vertically resizes window
if( isOnResizeBorder )
return HTTOP;
boolean isOnTitleBar = (pt.y < titleBarHeight);
if( isOnTitleBar ) {
// use a second reference to the array to avoid that it can be changed
// in another thread while processing the array
Rectangle[] hitTestSpots2 = hitTestSpots;
for( Rectangle spot : hitTestSpots2 ) {
if( spot.contains( sx, sy ) )
// return HTCLIENT if mouse is over any Swing component in title bar
// that processes mouse events (e.g. buttons, menus, etc)
// - Windows ignores mouse events in this area
try {
if( captionHitTestCallback != null && !captionHitTestCallback.test( pt ) )
return HTCLIENT;
} catch( Throwable ex ) {
// ignore
}
return isOnResizeBorder ? HTTOP : HTCAPTION;
// return HTCAPTION if mouse is over title bar
// - right-click shows system menu
// - double-left-click maximizes/restores window size
return HTCAPTION;
}
return isOnResizeBorder ? HTTOP : HTCLIENT;
// return HTCLIENT
// - Windows ignores mouse events in this area
return HTCLIENT;
}
private boolean contains( Rectangle rect, int x, int y ) {
return (rect != null && rect.contains( x, y ) );
private boolean contains( Rectangle rect, Point pt ) {
return (rect != null && rect.contains( pt ) );
}
/**

View File

@@ -0,0 +1,219 @@
/*
* Copyright 2024 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.JComponent;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.SystemInfo;
/**
* @author Karl Tauber
*/
class FullWindowContentSupport
{
private static final String KEY_DEBUG_SHOW_PLACEHOLDERS = "FlatLaf.debug.panel.showPlaceholders";
private static ArrayList<WeakReference<JComponent>> placeholders = new ArrayList<>();
static Dimension getPlaceholderPreferredSize( JComponent c, String options ) {
JRootPane rootPane;
Rectangle bounds;
if( !options.startsWith( SystemInfo.isMacOS ? "mac" : "win" ) ||
!c.isDisplayable() ||
(rootPane = SwingUtilities.getRootPane( c )) == null ||
(bounds = (Rectangle) rootPane.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS )) == null )
return new Dimension( 0, 0 );
if( options.length() > 3 ) {
if( (options.contains( "leftToRight" ) && !c.getComponentOrientation().isLeftToRight()) ||
(options.contains( "rightToLeft" ) && c.getComponentOrientation().isLeftToRight()) )
return new Dimension( 0, 0 );
}
// On macOS, the client property is updated very late when toggling full screen,
// which results in "jumping" layout after full screen toggle finished.
// To avoid that, get up-to-date buttons bounds from macOS.
if( SystemInfo.isMacFullWindowContentSupported && FlatNativeMacLibrary.isLoaded() ) {
Rectangle r = FlatNativeMacLibrary.getWindowButtonsBounds( SwingUtilities.windowForComponent( c ) );
if( r != null )
bounds = r;
}
int width = bounds.width;
int height = bounds.height;
if( options.length() > 3 ) {
if( width == 0 && options.contains( "zeroInFullScreen" ) )
height = 0;
if( options.contains( "horizontal" ) )
height = 0;
if( options.contains( "vertical" ) )
width = 0;
}
return new Dimension( width, height );
}
static void registerPlaceholder( JComponent c ) {
synchronized( placeholders ) {
if( indexOfPlaceholder( c ) < 0 )
placeholders.add( new WeakReference<>( c ) );
}
}
static void unregisterPlaceholder( JComponent c ) {
synchronized( placeholders ) {
int index = indexOfPlaceholder( c );
if( index >= 0 )
placeholders.remove( index );
}
}
private static int indexOfPlaceholder( JComponent c ) {
int size = placeholders.size();
for( int i = 0; i < size; i++ ) {
if( placeholders.get( i ).get() == c )
return i;
}
return -1;
}
static void revalidatePlaceholders( Component container ) {
synchronized( placeholders ) {
if( placeholders.isEmpty() )
return;
for( Iterator<WeakReference<JComponent>> it = placeholders.iterator(); it.hasNext(); ) {
WeakReference<JComponent> ref = it.next();
JComponent c = ref.get();
// remove already released placeholder
if( c == null ) {
it.remove();
continue;
}
// revalidate placeholder if is in given container
if( SwingUtilities.isDescendingFrom( c, container ) )
c.revalidate();
}
}
}
static ComponentListener macInstallListeners( JRootPane rootPane ) {
ComponentListener l = new ComponentAdapter() {
boolean lastFullScreen;
@Override
public void componentResized( ComponentEvent e ) {
Window window = SwingUtilities.windowForComponent( rootPane );
if( window == null )
return;
boolean fullScreen = FlatNativeMacLibrary.isLoaded() && FlatNativeMacLibrary.isWindowFullScreen( window );
if( fullScreen == lastFullScreen )
return;
lastFullScreen = fullScreen;
macUpdateFullWindowContentButtonsBoundsProperty( rootPane );
}
};
rootPane.addComponentListener( l );
return l;
}
static void macUninstallListeners( JRootPane rootPane, ComponentListener l ) {
if( l != null )
rootPane.removeComponentListener( l );
}
static void macUpdateFullWindowContentButtonsBoundsProperty( JRootPane rootPane ) {
if( !SystemInfo.isMacFullWindowContentSupported || !rootPane.isDisplayable() )
return;
Rectangle bounds = null;
if( FlatClientProperties.clientPropertyBoolean( rootPane, "apple.awt.fullWindowContent", false ) ) {
bounds = FlatNativeMacLibrary.isLoaded()
? FlatNativeMacLibrary.getWindowButtonsBounds( SwingUtilities.windowForComponent( rootPane ) )
: new Rectangle( 68, 28 ); // default size
}
rootPane.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS, bounds );
}
static void macUninstallFullWindowContentButtonsBoundsProperty( JRootPane rootPane ) {
if( !SystemInfo.isMacFullWindowContentSupported )
return;
rootPane.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS, null );
}
static void debugPaint( Graphics g, JComponent c ) {
if( !UIManager.getBoolean( KEY_DEBUG_SHOW_PLACEHOLDERS ) )
return;
int width = c.getWidth();
int height = c.getHeight();
if( width <= 0 || height <= 0 )
return;
// draw red figure
g.setColor( Color.red );
debugPaintRect( g, new Rectangle( width, height ) );
// draw magenta figure if buttons bounds are not equal to placeholder bounds
JRootPane rootPane;
Rectangle bounds;
if( (rootPane = SwingUtilities.getRootPane( c )) != null &&
(bounds = (Rectangle) rootPane.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS )) != null &&
(bounds.width != width || bounds.height != height) )
{
g.setColor( Color.magenta );
debugPaintRect( g, SwingUtilities.convertRectangle( rootPane, bounds, c ) );
}
}
private static void debugPaintRect( Graphics g, Rectangle r ) {
// draw rectangle
g.drawRect( r.x, r.y, r.width - 1, r.height - 1 );
// draw diagonal cross
int x2 = r.x + r.width - 1;
int y2 = r.y + r.height - 1;
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
g.drawLine( r.x, r.y, x2, y2 );
g.drawLine( r.x, y2, x2, r.y );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
}

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.ui;
import java.io.File;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -25,6 +26,7 @@ import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.filechooser.FileSystemView;
import javax.swing.plaf.ComponentUI;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.util.LoggingFacade;
@@ -88,4 +90,64 @@ public class JavaCompatibility2
return null;
}
}
/**
* Java 8 - 11 on Windows: sun.awt.shell.ShellFolder.get( "fileChooserShortcutPanelFolders" )
* <br>
* Java 12: javax.swing.filechooser.FileSystemView.getChooserShortcutPanelFiles()
*
* @since 3.4
*/
public static File[] getChooserShortcutPanelFiles( FileSystemView fsv ) {
try {
if( SystemInfo.isJava_12_orLater ) {
Method m = fsv.getClass().getMethod( "getChooserShortcutPanelFiles" );
File[] files = (File[]) m.invoke( fsv );
// on macOS and Linux, files consists only of the user home directory
if( files.length == 1 && files[0].equals( new File( System.getProperty( "user.home" ) ) ) )
files = new File[0];
return files;
} else if( SystemInfo.isWindows ) {
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
Method m = cls.getMethod( "get", String.class );
return (File[]) m.invoke( null, "fileChooserShortcutPanelFolders" );
}
} catch( IllegalAccessException ex ) {
// do not log because access may be denied via VM option '--illegal-access=deny'
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
// fallback
return new File[0];
}
/**
* Java 8: sun.awt.shell.ShellFolder.get( "fileChooserComboBoxFolders" )
* <br>
* Java 9: javax.swing.filechooser.FileSystemView.getChooserComboBoxFiles()
*
* @since 3.4
*/
public static File[] getChooserComboBoxFiles( FileSystemView fsv ) {
try {
if( SystemInfo.isJava_9_orLater ) {
Method m = fsv.getClass().getMethod( "getChooserComboBoxFiles" );
return (File[]) m.invoke( fsv );
} else {
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
Method m = cls.getMethod( "get", String.class );
return (File[]) m.invoke( null, "fileChooserComboBoxFolders" );
}
} catch( IllegalAccessException ex ) {
// do not log because access may be denied via VM option '--illegal-access=deny'
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
// fallback
return new File[0];
}
}

View File

@@ -739,6 +739,8 @@ Table.rowHeight = 20
Table.showHorizontalLines = false
Table.showVerticalLines = false
Table.showTrailingVerticalLine = false
Table.paintOutsideAlternateRows = false
Table.editorSelectAllOnStartEditing = true
Table.consistentHomeEndKeyBehavior = true
Table.intercellSpacing = 0,0
Table.scrollPaneBorder = com.formdev.flatlaf.ui.FlatScrollPaneBorder
@@ -829,7 +831,6 @@ TitlePane.centerTitleIfMenuBarEmbedded = true
TitlePane.showIconBesideTitle = false
TitlePane.menuBarTitleGap = 40
TitlePane.menuBarTitleMinimumGap = 12
TitlePane.menuBarResizeHeight = 4
TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon
TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon
TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon

View File

@@ -73,6 +73,7 @@ class DemoFrame
initComponents();
updateFontMenuItems();
initAccentColors();
initFullWindowContent();
controlBar.initialize( this, tabbedPane );
setIconImages( FlatSVGUtils.createWindowIconImages( "/com/formdev/flatlaf/demo/FlatLaf.svg" ) );
@@ -89,24 +90,23 @@ class DemoFrame
// do not use HTML text in menu items because this is not supported in macOS screen menu
htmlMenuItem.setText( "some text" );
JRootPane rootPane = getRootPane();
if( SystemInfo.isMacFullWindowContentSupported ) {
// expand window content into window title bar and make title bar transparent
getRootPane().putClientProperty( "apple.awt.fullWindowContent", true );
getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true );
rootPane.putClientProperty( "apple.awt.fullWindowContent", true );
rootPane.putClientProperty( "apple.awt.transparentTitleBar", true );
rootPane.putClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING, FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_LARGE );
// hide window title
if( SystemInfo.isJava_17_orLater )
getRootPane().putClientProperty( "apple.awt.windowTitleVisible", false );
rootPane.putClientProperty( "apple.awt.windowTitleVisible", false );
else
setTitle( null );
// add gap to left side of toolbar
toolBar.add( Box.createHorizontalStrut( 70 ), 0 );
}
// enable full screen mode for this window (for Java 8 - 10; not necessary for Java 11+)
if( !SystemInfo.isJava_11_orLater )
getRootPane().putClientProperty( "apple.awt.fullscreenable", true );
rootPane.putClientProperty( "apple.awt.fullscreenable", true );
}
// integrate into macOS screen menu
@@ -461,9 +461,37 @@ class DemoFrame
accentColorButtons[i].setVisible( isAccentColorSupported );
}
private void initFullWindowContent() {
if( !supportsFlatLafWindowDecorations() )
return;
// create fullWindowContent mode toggle button
Icon expandIcon = new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/expand.svg" );
Icon collapseIcon = new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/collapse.svg" );
JToggleButton fullWindowContentButton = new JToggleButton( expandIcon );
fullWindowContentButton.setToolTipText( "Toggle full window content" );
fullWindowContentButton.addActionListener( e -> {
boolean fullWindowContent = fullWindowContentButton.isSelected();
fullWindowContentButton.setIcon( fullWindowContent ? collapseIcon : expandIcon );
menuBar.setVisible( !fullWindowContent );
toolBar.setVisible( !fullWindowContent );
getRootPane().putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT, fullWindowContent );
} );
// add fullWindowContent mode toggle button to tabbed pane
JToolBar trailingToolBar = new JToolBar();
trailingToolBar.add( Box.createGlue() );
trailingToolBar.add( fullWindowContentButton );
tabbedPane.putClientProperty( FlatClientProperties.TABBED_PANE_TRAILING_COMPONENT, trailingToolBar );
}
private boolean supportsFlatLafWindowDecorations() {
return FlatLaf.supportsNativeWindowDecorations() || (SystemInfo.isLinux && JFrame.isDefaultLookAndFeelDecorated());
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JMenuBar menuBar1 = new JMenuBar();
menuBar = new JMenuBar();
JMenu fileMenu = new JMenu();
JMenuItem newMenuItem = new JMenuItem();
JMenuItem openMenuItem = new JMenuItem();
@@ -509,6 +537,8 @@ class DemoFrame
JMenuItem showUIDefaultsInspectorMenuItem = new JMenuItem();
JMenu helpMenu = new JMenu();
aboutMenuItem = new JMenuItem();
JPanel toolBarPanel = new JPanel();
JPanel macFullWindowContentButtonsPlaceholder = new JPanel();
toolBar = new JToolBar();
JButton backButton = new JButton();
JButton forwardButton = new JButton();
@@ -524,8 +554,10 @@ class DemoFrame
DataComponentsPanel dataComponentsPanel = new DataComponentsPanel();
TabsPanel tabsPanel = new TabsPanel();
OptionPanePanel optionPanePanel = new OptionPanePanel();
ExtrasPanel extrasPanel1 = new ExtrasPanel();
ExtrasPanel extrasPanel = new ExtrasPanel();
controlBar = new ControlBar();
JPanel themesPanelPanel = new JPanel();
JPanel winFullWindowContentButtonsPlaceholder = new JPanel();
themesPanel = new IJThemesPanel();
//======== this ========
@@ -534,7 +566,7 @@ class DemoFrame
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
//======== menuBar1 ========
//======== menuBar ========
{
//======== fileMenu ========
@@ -579,7 +611,7 @@ class DemoFrame
exitMenuItem.addActionListener(e -> exitActionPerformed());
fileMenu.add(exitMenuItem);
}
menuBar1.add(fileMenu);
menuBar.add(fileMenu);
//======== editMenu ========
{
@@ -632,7 +664,7 @@ class DemoFrame
deleteMenuItem.addActionListener(e -> menuItemActionPerformed(e));
editMenu.add(deleteMenuItem);
}
menuBar1.add(editMenu);
menuBar.add(editMenu);
//======== viewMenu ========
{
@@ -732,7 +764,7 @@ class DemoFrame
radioButtonMenuItem3.addActionListener(e -> menuItemActionPerformed(e));
viewMenu.add(radioButtonMenuItem3);
}
menuBar1.add(viewMenu);
menuBar.add(viewMenu);
//======== fontMenu ========
{
@@ -756,7 +788,7 @@ class DemoFrame
decrFontMenuItem.addActionListener(e -> decrFont());
fontMenu.add(decrFontMenuItem);
}
menuBar1.add(fontMenu);
menuBar.add(fontMenu);
//======== optionsMenu ========
{
@@ -808,7 +840,7 @@ class DemoFrame
showUIDefaultsInspectorMenuItem.addActionListener(e -> showUIDefaultsInspector());
optionsMenu.add(showUIDefaultsInspectorMenuItem);
}
menuBar1.add(optionsMenu);
menuBar.add(optionsMenu);
//======== helpMenu ========
{
@@ -821,54 +853,66 @@ class DemoFrame
aboutMenuItem.addActionListener(e -> aboutActionPerformed());
helpMenu.add(aboutMenuItem);
}
menuBar1.add(helpMenu);
menuBar.add(helpMenu);
}
setJMenuBar(menuBar1);
setJMenuBar(menuBar);
//======== toolBar ========
//======== toolBarPanel ========
{
toolBar.setMargin(new Insets(3, 3, 3, 3));
toolBarPanel.setLayout(new BorderLayout());
//---- backButton ----
backButton.setToolTipText("Back");
backButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/back.svg"));
toolBar.add(backButton);
//======== macFullWindowContentButtonsPlaceholder ========
{
macFullWindowContentButtonsPlaceholder.setLayout(new FlowLayout());
}
toolBarPanel.add(macFullWindowContentButtonsPlaceholder, BorderLayout.WEST);
//---- forwardButton ----
forwardButton.setToolTipText("Forward");
forwardButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/forward.svg"));
toolBar.add(forwardButton);
toolBar.addSeparator();
//======== toolBar ========
{
toolBar.setMargin(new Insets(3, 3, 3, 3));
//---- cutButton ----
cutButton.setToolTipText("Cut");
cutButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-cut.svg"));
toolBar.add(cutButton);
//---- backButton ----
backButton.setToolTipText("Back");
backButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/back.svg"));
toolBar.add(backButton);
//---- copyButton ----
copyButton.setToolTipText("Copy");
copyButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/copy.svg"));
toolBar.add(copyButton);
//---- forwardButton ----
forwardButton.setToolTipText("Forward");
forwardButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/forward.svg"));
toolBar.add(forwardButton);
toolBar.addSeparator();
//---- pasteButton ----
pasteButton.setToolTipText("Paste");
pasteButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-paste.svg"));
toolBar.add(pasteButton);
toolBar.addSeparator();
//---- cutButton ----
cutButton.setToolTipText("Cut");
cutButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-cut.svg"));
toolBar.add(cutButton);
//---- refreshButton ----
refreshButton.setToolTipText("Refresh");
refreshButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/refresh.svg"));
toolBar.add(refreshButton);
toolBar.addSeparator();
//---- copyButton ----
copyButton.setToolTipText("Copy");
copyButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/copy.svg"));
toolBar.add(copyButton);
//---- showToggleButton ----
showToggleButton.setSelected(true);
showToggleButton.setToolTipText("Show Details");
showToggleButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/show.svg"));
toolBar.add(showToggleButton);
//---- pasteButton ----
pasteButton.setToolTipText("Paste");
pasteButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-paste.svg"));
toolBar.add(pasteButton);
toolBar.addSeparator();
//---- refreshButton ----
refreshButton.setToolTipText("Refresh");
refreshButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/refresh.svg"));
toolBar.add(refreshButton);
toolBar.addSeparator();
//---- showToggleButton ----
showToggleButton.setSelected(true);
showToggleButton.setToolTipText("Show Details");
showToggleButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/show.svg"));
toolBar.add(showToggleButton);
}
toolBarPanel.add(toolBar, BorderLayout.CENTER);
}
contentPane.add(toolBar, BorderLayout.NORTH);
contentPane.add(toolBarPanel, BorderLayout.PAGE_START);
//======== contentPanel ========
{
@@ -888,13 +932,25 @@ class DemoFrame
tabbedPane.addTab("Data Components", dataComponentsPanel);
tabbedPane.addTab("Tabs", tabsPanel);
tabbedPane.addTab("Option Pane", optionPanePanel);
tabbedPane.addTab("Extras", extrasPanel1);
tabbedPane.addTab("Extras", extrasPanel);
}
contentPanel.add(tabbedPane, "cell 0 0");
}
contentPane.add(contentPanel, BorderLayout.CENTER);
contentPane.add(controlBar, BorderLayout.SOUTH);
contentPane.add(themesPanel, BorderLayout.EAST);
contentPane.add(controlBar, BorderLayout.PAGE_END);
//======== themesPanelPanel ========
{
themesPanelPanel.setLayout(new BorderLayout());
//======== winFullWindowContentButtonsPlaceholder ========
{
winFullWindowContentButtonsPlaceholder.setLayout(new FlowLayout());
}
themesPanelPanel.add(winFullWindowContentButtonsPlaceholder, BorderLayout.NORTH);
themesPanelPanel.add(themesPanel, BorderLayout.CENTER);
}
contentPane.add(themesPanelPanel, BorderLayout.LINE_END);
//---- buttonGroup1 ----
ButtonGroup buttonGroup1 = new ButtonGroup();
@@ -909,8 +965,8 @@ class DemoFrame
usersButton.setButtonType( ButtonType.toolBarButton );
usersButton.setFocusable( false );
usersButton.addActionListener( e -> JOptionPane.showMessageDialog( null, "Hello User! How are you?", "User", JOptionPane.INFORMATION_MESSAGE ) );
menuBar1.add( Box.createGlue() );
menuBar1.add( usersButton );
menuBar.add( Box.createGlue() );
menuBar.add( usersButton );
cutMenuItem.addActionListener( new DefaultEditorKit.CutAction() );
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
@@ -922,7 +978,7 @@ class DemoFrame
for( int i = 1; i <= 100; i++ )
scrollingPopupMenu.add( "Item " + i );
if( FlatLaf.supportsNativeWindowDecorations() || (SystemInfo.isLinux && JFrame.isDefaultLookAndFeelDecorated()) ) {
if( supportsFlatLafWindowDecorations() ) {
if( SystemInfo.isLinux )
unsupported( windowDecorationsCheckBoxMenuItem );
else
@@ -943,6 +999,17 @@ class DemoFrame
if( "false".equals( System.getProperty( "flatlaf.animatedLafChange" ) ) )
animatedLafChangeMenuItem.setSelected( false );
// on macOS, panel left to toolBar is a placeholder for title bar buttons in fullWindowContent mode
macFullWindowContentButtonsPlaceholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac zeroInFullScreen" );
// on Windows/Linux, panel above themesPanel is a placeholder for title bar buttons in fullWindowContent mode
winFullWindowContentButtonsPlaceholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "win" );
// uncomment this line to see title bar buttons placeholders in fullWindowContent mode
// UIManager.put( "FlatLaf.debug.panel.showPlaceholders", true );
// remove contentPanel bottom insets
MigLayout layout = (MigLayout) contentPanel.getLayout();
LC lc = ConstraintParser.parseLayoutConstraint( (String) layout.getLayoutConstraints() );
@@ -963,6 +1030,7 @@ class DemoFrame
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JMenuBar menuBar;
private JMenuItem exitMenuItem;
private JMenu scrollingPopupMenu;
private JMenuItem htmlMenuItem;

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.1.0.0.283" Java: "19.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.1.0.348" Java: "21.0.1" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -12,59 +12,69 @@ new FormModel {
"defaultCloseOperation": 2
"$locationPolicy": 2
"$sizePolicy": 2
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
name: "toolBar"
"margin": new java.awt.Insets( 3, 3, 3, 3 )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JButton" ) {
name: "backButton"
"toolTipText": "Back"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/back.svg" )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "toolBarPanel"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "macFullWindowContentButtonsPlaceholder"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "West"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "forwardButton"
"toolTipText": "Forward"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/forward.svg" )
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator5"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "cutButton"
"toolTipText": "Cut"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-cut.svg" )
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "copyButton"
"toolTipText": "Copy"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/copy.svg" )
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "pasteButton"
"toolTipText": "Paste"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-paste.svg" )
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator6"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "refreshButton"
"toolTipText": "Refresh"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/refresh.svg" )
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator7"
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "showToggleButton"
"selected": true
"toolTipText": "Show Details"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/show.svg" )
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
name: "toolBar"
"margin": new java.awt.Insets( 3, 3, 3, 3 )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JButton" ) {
name: "backButton"
"toolTipText": "Back"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/back.svg" )
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "forwardButton"
"toolTipText": "Forward"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/forward.svg" )
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator5"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "cutButton"
"toolTipText": "Cut"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-cut.svg" )
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "copyButton"
"toolTipText": "Copy"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/copy.svg" )
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "pasteButton"
"toolTipText": "Paste"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-paste.svg" )
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator6"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "refreshButton"
"toolTipText": "Refresh"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/refresh.svg" )
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator7"
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "showToggleButton"
"selected": true
"toolTipText": "Show Details"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/show.svg" )
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "North"
"value": "First"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
@@ -105,7 +115,7 @@ new FormModel {
"title": "Option Pane"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.extras.ExtrasPanel" ) {
name: "extrasPanel1"
name: "extrasPanel"
}, new FormLayoutConstraints( null ) {
"title": "Extras"
} )
@@ -121,19 +131,32 @@ new FormModel {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "South"
"value": "Last"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel" ) {
name: "themesPanel"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.variableModifiers": 0
}
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "themesPanelPanel"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "winFullWindowContentButtonsPlaceholder"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "North"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel" ) {
name: "themesPanel"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.variableModifiers": 0
}
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "East"
"value": "After"
} )
menuBar: new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) {
name: "menuBar1"
name: "menuBar"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "fileMenu"
"text": "File"

View File

@@ -0,0 +1,4 @@
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.5 14.5L6 10M14.5 1.5L9.99998 6.00001M2.5 9.50001H6.5L6.5 13.5M13.5 6.5L9.5 6.50003V2.5" stroke="#6E6E6E" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 396 B

View File

@@ -0,0 +1,4 @@
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.5 9.5L2 14M9.50003 6.50004L14 2.00001M5.5 14.5H1.5V10.5M10.5 1.5L14.5 1.50002V5.5" stroke="#6E6E6E" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 391 B

View File

@@ -25,6 +25,7 @@ import java.awt.Image;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.LinearGradientPaint;
import java.awt.image.BufferedImage;
import java.awt.image.RGBImageFilter;
import java.io.File;
@@ -62,6 +63,8 @@ public class FlatSVGIcon
extends ImageIcon
implements DisabledIconProvider
{
private static boolean loggingEnabled = true;
private static boolean svgCacheEnabled = true;
// cache that uses soft references for values, which allows freeing SVG documents if no longer used
private static final SoftCache<String, SVGDocument> svgCache = new SoftCache<>();
private static final SVGLoader svgLoader = new SVGLoader();
@@ -271,7 +274,8 @@ public class FlatSVGIcon
if( document == null ) {
loadFailed = true;
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: failed to load SVG icon from input stream", null );
if( loggingEnabled )
LoggingFacade.INSTANCE.logConfig( "FlatSVGIcon: failed to load SVG icon from input stream", null );
}
}
}
@@ -449,8 +453,9 @@ public class FlatSVGIcon
* @param colorFilter The color filter
* @since 1.2
*/
public void setColorFilter( ColorFilter colorFilter ) {
public FlatSVGIcon setColorFilter( ColorFilter colorFilter ) {
this.colorFilter = colorFilter;
return this;
}
private void update() {
@@ -474,7 +479,8 @@ public class FlatSVGIcon
if( url == null ) {
loadFailed = true;
LoggingFacade.INSTANCE.logConfig( "FlatSVGIcon: resource '" + name + "' not found (if using Java modules, check whether icon package is opened in module-info.java)", null );
if( loggingEnabled )
LoggingFacade.INSTANCE.logConfig( "FlatSVGIcon: resource '" + name + "' not found (if using Java modules, check whether icon package is opened in module-info.java)", null );
return;
}
}
@@ -484,6 +490,9 @@ public class FlatSVGIcon
}
static synchronized SVGDocument loadSVG( URL url ) {
if( !svgCacheEnabled )
return loadSVGUncached( url );
// get from our cache
String cacheKey = url.toString();
SVGDocument document = svgCache.get( cacheKey );
@@ -491,18 +500,25 @@ public class FlatSVGIcon
return document;
// load SVG document
document = svgLoader.load( url );
if( document == null ) {
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: failed to load '" + url + "'", null );
return null;
}
document = loadSVGUncached( url );
svgCache.put( cacheKey, document );
return document;
}
private static SVGDocument loadSVGUncached( URL url ) {
SVGDocument document = svgLoader.load( url );
if( document == null ) {
if( loggingEnabled )
LoggingFacade.INSTANCE.logConfig( "FlatSVGIcon: failed to load '" + url + "'", null );
return null;
}
return document;
}
private URL getIconURL( String name, boolean dark ) {
if( dark ) {
int dotIndex = name.lastIndexOf( '.' );
@@ -611,7 +627,10 @@ public class FlatSVGIcon
}
private void paintSvgError( Graphics2D g, int x, int y ) {
g.setColor( Color.red );
if( g instanceof GraphicsFilter )
((GraphicsFilter)g).setColorUnfiltered( Color.red );
else
g.setColor( Color.red );
g.fillRect( x, y, getIconWidth(), getIconHeight() );
}
@@ -692,6 +711,34 @@ public class FlatSVGIcon
darkLaf = FlatLaf.isLafDark();
}
/** @since 3.4.1 */
public static boolean isLoggingEnabled() {
return loggingEnabled;
}
/** @since 3.4.1 */
public static void setLoggingEnabled( boolean loggingEnabled ) {
FlatSVGIcon.loggingEnabled = loggingEnabled;
}
/** @since 3.4.1 */
public static boolean isSVGDocumentEnabled() {
return svgCacheEnabled;
}
/** @since 3.4.1 */
public static void setSVGDocumentEnabled( boolean svgCacheEnabled ) {
FlatSVGIcon.svgCacheEnabled = svgCacheEnabled;
if( !svgCacheEnabled )
clearSVGDocumentCache();
}
/** @since 3.4.1 */
public static void clearSVGDocumentCache() {
svgCache.clear();
}
//---- class ColorFilter --------------------------------------------------
/**
@@ -978,10 +1025,23 @@ public class FlatSVGIcon
super.setColor( filterColor( c ) );
}
void setColorUnfiltered( Color c ) {
super.setColor( c );
}
@Override
public void setPaint( Paint paint ) {
if( paint instanceof Color )
paint = filterColor( (Color) paint );
else if( paint instanceof LinearGradientPaint ) {
LinearGradientPaint oldPaint = (LinearGradientPaint) paint;
Color[] newColors = filterColors( oldPaint.getColors() );
if( newColors != null ) {
paint = new LinearGradientPaint( oldPaint.getStartPoint(), oldPaint.getEndPoint(),
oldPaint.getFractions(), newColors, oldPaint.getCycleMethod(),
oldPaint.getColorSpace(), oldPaint.getTransform() );
}
}
super.setPaint( paint );
}
@@ -1001,5 +1061,15 @@ public class FlatSVGIcon
}
return color;
}
private Color[] filterColors( Color[] colors ) {
Color[] newColors = new Color[colors.length];
boolean changed = false;
for( int i = 0; i < colors.length; i++ ) {
newColors[i] = filterColor( colors[i] );
changed = (changed || newColors[i] != colors[i]);
}
return changed ? newColors : null;
}
}
}

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.extras.components;
import static com.formdev.flatlaf.FlatClientProperties.*;
import javax.swing.JSplitPane;
/**
@@ -26,6 +27,29 @@ import javax.swing.JSplitPane;
*/
public class FlatSplitPane
extends JSplitPane
implements FlatStyleableComponent
implements FlatComponentExtension, FlatStyleableComponent
{
// NOTE: enum names must be equal to allowed strings
/** @since 3.4.1 */ public enum ExpandableSide { both, left, right }
/**
* Returns what side of the spilt pane is allowed to expand
* via one-touch expanding arrow buttons.
*
* @since 3.4.1
*/
public ExpandableSide getExpandableSide() {
return getClientPropertyEnumString( SPLIT_PANE_EXPANDABLE_SIDE, ExpandableSide.class,
null, ExpandableSide.both );
}
/**
* Specifies what side of the spilt pane is allowed to expand
* via one-touch expanding arrow buttons.
*
* @since 3.4.1
*/
public void setExpandableSide( ExpandableSide expandableSide ) {
putClientPropertyEnumString( SPLIT_PANE_EXPANDABLE_SIDE, expandableSide );
}
}

View File

@@ -33,8 +33,8 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
testImplementation( libs.junit )
testRuntimeOnly( libs.junit.launcher )
}
flatlafModuleInfo {
@@ -56,7 +56,7 @@ tasks {
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}
withType<PublishToMavenRepository>().configureEach {
withType<AbstractPublishToMaven>().configureEach {
onlyIf { !rootProject.hasProperty( "skipFonts" ) }
}
}
@@ -73,8 +73,8 @@ publishing {
pom {
licenses {
license {
name.set( "SIL OPEN FONT LICENSE Version 1.1" )
url.set( "https://choosealicense.com/licenses/ofl-1.1/" )
name = "SIL OPEN FONT LICENSE Version 1.1"
url = "https://choosealicense.com/licenses/ofl-1.1/"
}
}
}

View File

@@ -33,8 +33,8 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
testImplementation( libs.junit )
testRuntimeOnly( libs.junit.launcher )
}
flatlafModuleInfo {
@@ -56,7 +56,7 @@ tasks {
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}
withType<PublishToMavenRepository>().configureEach {
withType<AbstractPublishToMaven>().configureEach {
onlyIf { !rootProject.hasProperty( "skipFonts" ) }
}
}
@@ -73,8 +73,8 @@ publishing {
pom {
licenses {
license {
name.set( "SIL OPEN FONT LICENSE Version 1.1" )
url.set( "https://choosealicense.com/licenses/ofl-1.1/" )
name = "SIL OPEN FONT LICENSE Version 1.1"
url = "https://choosealicense.com/licenses/ofl-1.1/"
}
}
}

View File

@@ -33,8 +33,8 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
testImplementation( libs.junit )
testRuntimeOnly( libs.junit.launcher )
}
flatlafModuleInfo {
@@ -56,7 +56,7 @@ tasks {
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}
withType<PublishToMavenRepository>().configureEach {
withType<AbstractPublishToMaven>().configureEach {
onlyIf { !rootProject.hasProperty( "skipFonts" ) }
}
}

View File

@@ -33,8 +33,8 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
testImplementation( libs.junit )
testRuntimeOnly( libs.junit.launcher )
}
flatlafModuleInfo {
@@ -56,7 +56,7 @@ tasks {
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}
withType<PublishToMavenRepository>().configureEach {
withType<AbstractPublishToMaven>().configureEach {
onlyIf { !rootProject.hasProperty( "skipFonts" ) }
}
}

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.jideoss.ui;
import static com.formdev.flatlaf.FlatClientProperties.COMPONENT_TITLE_BAR_CAPTION;
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER;
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_SHOW_TAB_SEPARATORS;
import static com.formdev.flatlaf.FlatClientProperties.clientPropertyBoolean;
@@ -30,6 +31,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseListener;
@@ -37,6 +39,7 @@ import java.awt.event.MouseMotionListener;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeListener;
import java.util.function.Function;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
@@ -100,6 +103,25 @@ public class FlatJideTabbedPaneUI
return new FlatJideTabbedPaneUI();
}
@Override
public void installUI( JComponent c ) {
super.installUI( c );
c.putClientProperty( COMPONENT_TITLE_BAR_CAPTION,
(Function<Point, Boolean>) pt -> {
if( tabForCoordinate( _tabPane, pt.x, pt.y ) >= 0 )
return false;
return null; // check children
} );
}
@Override
public void uninstallUI( JComponent c ) {
super.uninstallUI( c );
c.putClientProperty( COMPONENT_TITLE_BAR_CAPTION, null );
}
@Override
protected void installDefaults() {
super.installDefaults();

View File

@@ -32,8 +32,8 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.Timer;
@@ -164,7 +164,7 @@ public class FlatWindowsNativeWindowBorder
}
@Override
public void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
public void updateTitleBarInfo( Window window, int titleBarHeight, Predicate<Point> captionHitTestCallback,
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
Rectangle closeButtonBounds )
{
@@ -173,7 +173,7 @@ public class FlatWindowsNativeWindowBorder
return;
wndProc.titleBarHeight = titleBarHeight;
wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] );
wndProc.captionHitTestCallback = captionHitTestCallback;
wndProc.appIconBounds = cloneRectange( appIconBounds );
wndProc.minimizeButtonBounds = cloneRectange( minimizeButtonBounds );
wndProc.maximizeButtonBounds = cloneRectange( maximizeButtonBounds );
@@ -351,7 +351,7 @@ public class FlatWindowsNativeWindowBorder
// Swing coordinates/values may be scaled on a HiDPI screen
private int titleBarHeight;
private Rectangle[] hitTestSpots;
private Predicate<Point> captionHitTestCallback;
private Rectangle appIconBounds;
private Rectangle minimizeButtonBounds;
private Rectangle maximizeButtonBounds;
@@ -644,53 +644,65 @@ public class FlatWindowsNativeWindowBorder
// scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen
Point pt = scaleDown( x, y );
int sx = pt.x;
int sy = pt.y;
// return HTSYSMENU if mouse is over application icon
// - left-click on HTSYSMENU area shows system menu
// - double-left-click sends WM_CLOSE
if( contains( appIconBounds, sx, sy ) )
if( contains( appIconBounds, pt ) )
return new LRESULT( HTSYSMENU );
// return HTMINBUTTON if mouse is over minimize button
// - hovering mouse over HTMINBUTTON area shows tooltip on Windows 10/11
if( contains( minimizeButtonBounds, sx, sy ) )
if( contains( minimizeButtonBounds, pt ) )
return new LRESULT( HTMINBUTTON );
// return HTMAXBUTTON if mouse is over maximize/restore button
// - hovering mouse over HTMAXBUTTON area shows tooltip on Windows 10
// - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
if( contains( maximizeButtonBounds, sx, sy ) )
if( contains( maximizeButtonBounds, pt ) )
return new LRESULT( HTMAXBUTTON );
// return HTCLOSE if mouse is over close button
// - hovering mouse over HTCLOSE area shows tooltip on Windows 10/11
if( contains( closeButtonBounds, sx, sy ) )
if( contains( closeButtonBounds, pt ) )
return new LRESULT( HTCLOSE );
int resizeBorderHeight = getResizeHandleHeight();
boolean isOnResizeBorder = (y < resizeBorderHeight) &&
(User32.INSTANCE.GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
boolean isOnTitleBar = (sy < titleBarHeight);
// return HTTOP if mouse is over top resize border
// - hovering mouse shows vertical resize cursor
// - left-click and drag vertically resizes window
if( isOnResizeBorder )
return new LRESULT( HTTOP );
boolean isOnTitleBar = (pt.y < titleBarHeight);
if( isOnTitleBar ) {
// use a second reference to the array to avoid that it can be changed
// in another thread while processing the array
Rectangle[] hitTestSpots2 = hitTestSpots;
for( Rectangle spot : hitTestSpots2 ) {
if( spot.contains( sx, sy ) )
// return HTCLIENT if mouse is over any Swing component in title bar
// that processes mouse events (e.g. buttons, menus, etc)
// - Windows ignores mouse events in this area
try {
if( captionHitTestCallback != null && !captionHitTestCallback.test( pt ) )
return new LRESULT( HTCLIENT );
} catch( Throwable ex ) {
// ignore
}
return new LRESULT( isOnResizeBorder ? HTTOP : HTCAPTION );
// return HTCAPTION if mouse is over title bar
// - right-click shows system menu
// - double-left-click maximizes/restores window size
return new LRESULT( HTCAPTION );
}
return new LRESULT( isOnResizeBorder ? HTTOP : HTCLIENT );
// return HTCLIENT
// - Windows ignores mouse events in this area
return new LRESULT( HTCLIENT );
}
private boolean contains( Rectangle rect, int x, int y ) {
return (rect != null && rect.contains( x, y ) );
private boolean contains( Rectangle rect, Point pt ) {
return (rect != null && rect.contains( pt ) );
}
/**

View File

@@ -21,11 +21,14 @@ plugins {
}
flatlafJniHeaders {
headers = listOf( "com_formdev_flatlaf_ui_FlatNativeLinuxLibrary.h" )
headers = listOf(
"com_formdev_flatlaf_ui_FlatNativeLibrary.h",
"com_formdev_flatlaf_ui_FlatNativeLinuxLibrary.h"
)
}
library {
targetMachines.set( listOf( machines.linux.x86_64 ) )
targetMachines = listOf( machines.linux.x86_64 )
}
var javaHome = System.getProperty( "java.home" )

View File

@@ -0,0 +1,37 @@
/*
* 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.
*/
#include <jni.h>
#include "com_formdev_flatlaf_ui_FlatNativeLibrary.h"
/**
* @author Karl Tauber
*/
// increase this version if changing API or functionality of native library
// also update version in Java class com.formdev.flatlaf.ui.FlatNativeLinuxLibrary
#define API_VERSION_LINUX 3001
//---- JNI methods ------------------------------------------------------------
extern "C"
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLibrary_getApiVersion
( JNIEnv* env, jclass cls )
{
return API_VERSION_LINUX;
}

View File

@@ -0,0 +1,21 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_formdev_flatlaf_ui_FlatNativeLibrary */
#ifndef _Included_com_formdev_flatlaf_ui_FlatNativeLibrary
#define _Included_com_formdev_flatlaf_ui_FlatNativeLibrary
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_formdev_flatlaf_ui_FlatNativeLibrary
* Method: getApiVersion
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLibrary_getApiVersion
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -26,14 +26,17 @@ plugins {
}
flatlafJniHeaders {
headers = listOf( "com_formdev_flatlaf_ui_FlatNativeMacLibrary.h" )
headers = listOf(
"com_formdev_flatlaf_ui_FlatNativeLibrary.h",
"com_formdev_flatlaf_ui_FlatNativeMacLibrary.h"
)
}
library {
targetMachines.set( listOf(
targetMachines = listOf(
machines.macOS.architecture( "arm64" ),
machines.macOS.x86_64
) )
)
}
var javaHome = System.getProperty( "java.home" )

View File

@@ -41,4 +41,6 @@
}
jfieldID getFieldID( JNIEnv *env, const char* className, const char* fieldName, const char* fieldSignature );
jclass findClass( JNIEnv *env, const char* className, bool globalRef );
jfieldID getFieldID( JNIEnv *env, jclass cls, const char* fieldName, const char* fieldSignature, bool staticField );
jmethodID getMethodID( JNIEnv *env, jclass cls, const char* methodName, const char* methodSignature, bool staticMethod );

View File

@@ -0,0 +1,21 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_formdev_flatlaf_ui_FlatNativeLibrary */
#ifndef _Included_com_formdev_flatlaf_ui_FlatNativeLibrary
#define _Included_com_formdev_flatlaf_ui_FlatNativeLibrary
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_formdev_flatlaf_ui_FlatNativeLibrary
* Method: getApiVersion
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLibrary_getApiVersion
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -7,6 +7,12 @@
#ifdef __cplusplus
extern "C" {
#endif
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_DEFAULT
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_DEFAULT 0L
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_MEDIUM
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_MEDIUM 1L
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_LARGE
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_LARGE 2L
/*
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
* Method: setWindowRoundedBorder
@@ -15,6 +21,38 @@ extern "C" {
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder
(JNIEnv *, jclass, jobject, jfloat, jfloat, jint);
/*
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
* Method: setWindowButtonsSpacing
* Signature: (Ljava/awt/Window;I)Z
*/
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowButtonsSpacing
(JNIEnv *, jclass, jobject, jint);
/*
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
* Method: getWindowButtonsBounds
* Signature: (Ljava/awt/Window;)Ljava/awt/Rectangle;
*/
JNIEXPORT jobject JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_getWindowButtonsBounds
(JNIEnv *, jclass, jobject);
/*
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
* Method: isWindowFullScreen
* Signature: (Ljava/awt/Window;)Z
*/
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_isWindowFullScreen
(JNIEnv *, jclass, jobject);
/*
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
* Method: toggleWindowFullScreen
* Signature: (Ljava/awt/Window;)Z
*/
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_toggleWindowFullScreen
(JNIEnv *, jclass, jobject);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,37 @@
/*
* 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.
*/
#include <jni.h>
#include "com_formdev_flatlaf_ui_FlatNativeLibrary.h"
/**
* @author Karl Tauber
*/
// increase this version if changing API or functionality of native library
// also update version in Java class com.formdev.flatlaf.ui.FlatNativeMacLibrary
#define API_VERSION_MACOS 2001
//---- JNI methods ------------------------------------------------------------
extern "C"
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLibrary_getApiVersion
( JNIEnv* env, jclass cls )
{
return API_VERSION_MACOS;
}

View File

@@ -21,8 +21,8 @@
* @author Karl Tauber
*/
jfieldID getFieldID( JNIEnv *env, const char* className, const char* fieldName, const char* fieldSignature ) {
// NSLog( @"getFieldID %s %s %s", className, fieldName, fieldSignature );
jclass findClass( JNIEnv *env, const char* className, bool globalRef ) {
// NSLog( @"findClass %s", className );
jclass cls = env->FindClass( className );
if( cls == NULL ) {
@@ -32,9 +32,23 @@ jfieldID getFieldID( JNIEnv *env, const char* className, const char* fieldName,
return NULL;
}
jfieldID fieldID = env->GetFieldID( cls, fieldName, fieldSignature );
if( globalRef )
cls = reinterpret_cast<jclass>( env->NewGlobalRef( cls ) );
return cls;
}
jfieldID getFieldID( JNIEnv *env, jclass cls, const char* fieldName, const char* fieldSignature, bool staticField ) {
// NSLog( @"getFieldID %s %s", fieldName, fieldSignature );
if( cls == NULL )
return NULL;
jfieldID fieldID = staticField
? env->GetStaticFieldID( cls, fieldName, fieldSignature )
: env->GetFieldID( cls, fieldName, fieldSignature );
if( fieldID == NULL ) {
NSLog( @"FlatLaf: failed to lookup field '%s' of type '%s' in class '%s'", fieldName, fieldSignature, className );
NSLog( @"FlatLaf: failed to lookup field '%s' of type '%s'", fieldName, fieldSignature );
env->ExceptionDescribe(); // print stack trace
env->ExceptionClear();
return NULL;
@@ -42,3 +56,22 @@ jfieldID getFieldID( JNIEnv *env, const char* className, const char* fieldName,
return fieldID;
}
jmethodID getMethodID( JNIEnv *env, jclass cls, const char* methodName, const char* methodSignature, bool staticMethod ) {
// NSLog( @"getMethodID %s %s", methodName, methodSignature );
if( cls == NULL )
return NULL;
jmethodID methodID = staticMethod
? env->GetStaticMethodID( cls, methodName, methodSignature )
: env->GetMethodID( cls, methodName, methodSignature );
if( methodID == NULL ) {
NSLog( @"FlatLaf: failed to lookup method '%s' of type '%s'", methodName, methodSignature );
env->ExceptionDescribe(); // print stack trace
env->ExceptionClear();
return NULL;
}
return methodID;
}

View File

@@ -15,6 +15,7 @@
*/
#import <Cocoa/Cocoa.h>
#import <objc/runtime.h>
#import <jni.h>
#import "JNIUtils.h"
#import "JNFRunLoop.h"
@@ -24,31 +25,70 @@
* @author Karl Tauber
*/
@interface WindowData : NSObject
// used when window is full screen
@property (nonatomic) int lastWindowButtonAreaWidth;
@property (nonatomic) int lastWindowTitleBarHeight;
// full screen observers
@property (nonatomic) id willEnterFullScreenObserver;
@property (nonatomic) id willExitFullScreenObserver;
@property (nonatomic) id didExitFullScreenObserver;
@end
@implementation WindowData
@end
// declare internal methods
NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window );
WindowData* getWindowData( NSWindow* nsWindow, bool allocate );
void setWindowButtonsHidden( NSWindow* nsWindow, bool hidden );
int getWindowButtonAreaWidth( NSWindow* nsWindow );
int getWindowTitleBarHeight( NSWindow* nsWindow );
bool isWindowFullScreen( NSWindow* nsWindow );
NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ) {
if( window == NULL )
return NULL;
// initialize field IDs (done only once because fields are static)
static jfieldID peerID = getFieldID( env, "java/awt/Component", "peer", "Ljava/awt/peer/ComponentPeer;" );
static jfieldID platformWindowID = getFieldID( env, "sun/lwawt/LWWindowPeer", "platformWindow", "Lsun/lwawt/PlatformWindow;" );
static jfieldID ptrID = getFieldID( env, "sun/lwawt/macosx/CFRetainedResource", "ptr", "J" );
// initialize class IDs (done only once because variables are static)
static jclass lwWindowPeerClass = findClass( env, "sun/lwawt/LWWindowPeer", true );
static jclass cfRetainedResourceClass = findClass( env, "sun/lwawt/macosx/CFRetainedResource", true );
if( lwWindowPeerClass == NULL || cfRetainedResourceClass == NULL )
return NULL;
// initialize field IDs (done only once because variables are static)
static jfieldID peerID = getFieldID( env, findClass( env, "java/awt/Component", false ), "peer", "Ljava/awt/peer/ComponentPeer;", false );
static jfieldID platformWindowID = getFieldID( env, lwWindowPeerClass, "platformWindow", "Lsun/lwawt/PlatformWindow;", false );
static jfieldID ptrID = getFieldID( env, cfRetainedResourceClass, "ptr", "J", false );
if( peerID == NULL || platformWindowID == NULL || ptrID == NULL )
return NULL;
// get field java.awt.Component.peer
jobject peer = env->GetObjectField( window, peerID );
if( peer == NULL )
if( peer == NULL || !env->IsInstanceOf( peer, lwWindowPeerClass ) )
return NULL;
// get field sun.lwawt.LWWindowPeer.platformWindow
jobject platformWindow = env->GetObjectField( peer, platformWindowID );
if( platformWindow == NULL )
if( platformWindow == NULL || !env->IsInstanceOf( platformWindow, cfRetainedResourceClass ) )
return NULL;
// get field sun.lwawt.macosx.CFRetainedResource.ptr
return (NSWindow *) jlong_to_ptr( env->GetLongField( platformWindow, ptrID ) );
}
WindowData* getWindowData( NSWindow* nsWindow, bool allocate ) {
static char key;
WindowData* windowData = objc_getAssociatedObject( nsWindow, &key );
if( windowData == NULL && allocate ) {
windowData = [WindowData new];
objc_setAssociatedObject( nsWindow, &key, windowData, OBJC_ASSOCIATION_RETAIN_NONATOMIC );
}
return windowData;
}
extern "C"
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder
( JNIEnv* env, jclass cls, jobject window, jfloat radius, jfloat borderWidth, jint borderColor )
@@ -87,3 +127,259 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setW
JNI_COCOA_EXIT()
return FALSE;
}
extern "C"
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowButtonsSpacing
( JNIEnv* env, jclass cls, jobject window, jint buttonsSpacing )
{
JNI_COCOA_ENTER()
NSWindow* nsWindow = getNSWindow( env, cls, window );
if( nsWindow == NULL )
return FALSE;
#define SPACING_DEFAULT com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_DEFAULT
#define SPACING_MEDIUM com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_MEDIUM
#define SPACING_LARGE com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_LARGE
bool isMacOS_11_orLater = @available( macOS 11, * );
if( !isMacOS_11_orLater && buttonsSpacing == SPACING_LARGE )
buttonsSpacing = SPACING_MEDIUM;
int oldButtonsSpacing = (nsWindow.toolbar != NULL)
? ((isMacOS_11_orLater && nsWindow.toolbarStyle == NSWindowToolbarStyleUnified)
? SPACING_LARGE
: SPACING_MEDIUM)
: SPACING_DEFAULT;
if( buttonsSpacing == oldButtonsSpacing )
return TRUE;
WindowData* windowData = getWindowData( nsWindow, true );
[FlatJNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
// NSLog( @"\n%@\n\n", [nsWindow.contentView.superview _subtreeDescription] );
// add/remove toolbar
NSToolbar* toolbar = NULL;
bool needsToolbar = (buttonsSpacing != SPACING_DEFAULT);
if( needsToolbar ) {
toolbar = [NSToolbar new];
toolbar.showsBaselineSeparator = NO; // necessary for older macOS versions
if( isWindowFullScreen( nsWindow ) )
toolbar.visible = NO;
}
nsWindow.toolbar = toolbar;
if( isMacOS_11_orLater ) {
nsWindow.toolbarStyle = (buttonsSpacing == SPACING_LARGE)
? NSWindowToolbarStyleUnified
: (buttonsSpacing == SPACING_MEDIUM)
? NSWindowToolbarStyleUnifiedCompact
: NSWindowToolbarStyleAutomatic;
}
windowData.lastWindowButtonAreaWidth = 0;
windowData.lastWindowTitleBarHeight = 0;
// NSLog( @"\n%@\n\n", [nsWindow.contentView.superview _subtreeDescription] );
// when window becomes full screen, it is necessary to hide the toolbar
// because it otherwise is shown non-transparent and hides Swing components
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
if( needsToolbar && windowData.willEnterFullScreenObserver == NULL ) {
// NSLog( @"add observers %@", nsWindow );
windowData.willEnterFullScreenObserver = [center addObserverForName:NSWindowWillEnterFullScreenNotification
object:nsWindow queue:nil usingBlock:^(NSNotification *note) {
// NSLog( @"enter full screen %@", nsWindow );
if( nsWindow.toolbar != NULL ) {
// remember button area width, which is used later when window exits full screen
// remembar title bar height so that "main" JToolBar keeps its height in full screen
windowData.lastWindowButtonAreaWidth = getWindowButtonAreaWidth( nsWindow );
windowData.lastWindowTitleBarHeight = getWindowTitleBarHeight( nsWindow );
// NSLog( @"%d %d", windowData.lastWindowButtonAreaWidth, windowData.lastWindowTitleBarHeight );
nsWindow.toolbar.visible = NO;
}
}];
windowData.willExitFullScreenObserver = [center addObserverForName:NSWindowWillExitFullScreenNotification
object:nsWindow queue:nil usingBlock:^(NSNotification *note) {
// NSLog( @"will exit full screen %@", nsWindow );
if( nsWindow.toolbar != NULL )
setWindowButtonsHidden( nsWindow, true );
}];
windowData.didExitFullScreenObserver = [center addObserverForName:NSWindowDidExitFullScreenNotification
object:nsWindow queue:nil usingBlock:^(NSNotification *note) {
// NSLog( @"exit full screen %@", nsWindow );
if( nsWindow.toolbar != NULL ) {
setWindowButtonsHidden( nsWindow, false );
nsWindow.toolbar.visible = YES;
}
windowData.lastWindowButtonAreaWidth = 0;
windowData.lastWindowTitleBarHeight = 0;
}];
} else if( !needsToolbar ) {
// NSLog( @"remove observers %@", nsWindow );
if( windowData.willEnterFullScreenObserver != NULL ) {
[center removeObserver:windowData.willEnterFullScreenObserver];
windowData.willEnterFullScreenObserver = nil;
}
if( windowData.willExitFullScreenObserver != NULL ) {
[center removeObserver:windowData.willExitFullScreenObserver];
windowData.willExitFullScreenObserver = nil;
}
if( windowData.didExitFullScreenObserver != NULL ) {
[center removeObserver:windowData.didExitFullScreenObserver];
windowData.didExitFullScreenObserver = nil;
}
}
}];
return TRUE;
JNI_COCOA_EXIT()
return FALSE;
}
void setWindowButtonsHidden( NSWindow* nsWindow, bool hidden ) {
// get buttons
NSView* buttons[3] = {
[nsWindow standardWindowButton:NSWindowCloseButton],
[nsWindow standardWindowButton:NSWindowMiniaturizeButton],
[nsWindow standardWindowButton:NSWindowZoomButton]
};
for( int i = 0; i < 3; i++ ) {
NSView* button = buttons[i];
if( button != NULL )
button.hidden = hidden;
}
}
extern "C"
JNIEXPORT jobject JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_getWindowButtonsBounds
( JNIEnv* env, jclass cls, jobject window )
{
JNI_COCOA_ENTER()
NSWindow* nsWindow = getNSWindow( env, cls, window );
if( nsWindow == NULL )
return NULL;
WindowData* windowData = getWindowData( nsWindow, false );
int width = 0;
int height = 0;
// get width
if( isWindowFullScreen( nsWindow ) ) {
// use zero if window is full screen because close/minimize/zoom buttons are hidden
width = 0;
} else if( windowData != NULL && windowData.lastWindowButtonAreaWidth > 0 ) {
// use remembered value if window is in transition from full screen to non-full screen
// because NSToolbar is not yet visible
width = windowData.lastWindowButtonAreaWidth;
} else
width = getWindowButtonAreaWidth( nsWindow );
// get height
if( windowData != NULL && windowData.lastWindowTitleBarHeight > 0 ) {
// use remembered value if window is full screen because NSToolbar is hidden
height = windowData.lastWindowTitleBarHeight;
} else
height = getWindowTitleBarHeight( nsWindow );
// initialize class and method ID (done only once because variables are static)
static jclass cls = findClass( env, "java/awt/Rectangle", true );
static jmethodID methodID = getMethodID( env, cls, "<init>", "(IIII)V", false );
if( cls == NULL || methodID == NULL )
return NULL;
// create and return Rectangle
return env->NewObject( cls, methodID, 0, 0, width, height );
JNI_COCOA_EXIT()
return NULL;
}
int getWindowButtonAreaWidth( NSWindow* nsWindow ) {
// get buttons
NSView* buttons[3] = {
[nsWindow standardWindowButton:NSWindowCloseButton],
[nsWindow standardWindowButton:NSWindowMiniaturizeButton],
[nsWindow standardWindowButton:NSWindowZoomButton]
};
// get most left and right coordinates
int left = -1;
int right = -1;
for( int i = 0; i < 3; i++ ) {
NSView* button = buttons[i];
if( button == NULL )
continue;
int x = [button convertRect: [button bounds] toView:button.superview].origin.x;
int width = button.bounds.size.width;
if( left == -1 || x < left )
left = x;
if( right == -1 || x + width > right )
right = x + width;
}
if( left == -1 || right == -1 )
return -1;
// 'right' is the actual button area width (from left window edge)
// adding 'left' to add same empty space on right side as on left side
return right + left;
}
int getWindowTitleBarHeight( NSWindow* nsWindow ) {
NSView* closeButton = [nsWindow standardWindowButton:NSWindowCloseButton];
if( closeButton == NULL )
return -1;
NSView* titlebar = closeButton.superview;
return titlebar.bounds.size.height;
}
extern "C"
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_isWindowFullScreen
( JNIEnv* env, jclass cls, jobject window )
{
JNI_COCOA_ENTER()
NSWindow* nsWindow = getNSWindow( env, cls, window );
if( nsWindow == NULL )
return FALSE;
return (jboolean) isWindowFullScreen( nsWindow );
JNI_COCOA_EXIT()
return FALSE;
}
bool isWindowFullScreen( NSWindow* nsWindow ) {
return ((nsWindow.styleMask & NSWindowStyleMaskFullScreen) != 0);
}
extern "C"
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_toggleWindowFullScreen
( JNIEnv* env, jclass cls, jobject window )
{
JNI_COCOA_ENTER()
NSWindow* nsWindow = getNSWindow( env, cls, window );
if( nsWindow == NULL )
return FALSE;
[FlatJNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
[nsWindow toggleFullScreen:nil];
}];
return TRUE;
JNI_COCOA_EXIT()
return FALSE;
}

View File

@@ -22,6 +22,7 @@ plugins {
flatlafJniHeaders {
headers = listOf(
"com_formdev_flatlaf_ui_FlatNativeLibrary.h",
"com_formdev_flatlaf_ui_FlatNativeWindowsLibrary.h",
"com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder.h",
"com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc.h"
@@ -29,11 +30,11 @@ flatlafJniHeaders {
}
library {
targetMachines.set( listOf(
targetMachines = listOf(
machines.windows.x86,
machines.windows.x86_64,
machines.windows.architecture( "aarch64" )
) )
)
}
var javaHome = System.getProperty( "java.home" )

View File

@@ -0,0 +1,37 @@
/*
* 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.
*/
#include <jni.h>
#include "com_formdev_flatlaf_ui_FlatNativeLibrary.h"
/**
* @author Karl Tauber
*/
// increase this version if changing API or functionality of native library
// also update version in Java class com.formdev.flatlaf.ui.FlatNativeWindowsLibrary
#define API_VERSION_WINDOWS 1001
//---- JNI methods ------------------------------------------------------------
extern "C"
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLibrary_getApiVersion
( JNIEnv* env, jclass cls )
{
return API_VERSION_WINDOWS;
}

View File

@@ -0,0 +1,21 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_formdev_flatlaf_ui_FlatNativeLibrary */
#ifndef _Included_com_formdev_flatlaf_ui_FlatNativeLibrary
#define _Included_com_formdev_flatlaf_ui_FlatNativeLibrary
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_formdev_flatlaf_ui_FlatNativeLibrary
* Method: getApiVersion
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLibrary_getApiVersion
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -41,6 +41,8 @@ dependencies {
implementation( libs.jide.oss )
implementation( libs.glazedlists )
implementation( libs.netbeans.api.awt )
components.all<TargetJvmVersion8Rule>()
}
applyLafs()
@@ -58,3 +60,13 @@ fun applyLafs() {
dependencies.implementation( parts[2] )
}
}
// rule that overrides 'org.gradle.jvm.version' with '8'
// (required for Radiance, which requires Java 9, but FlatLaf build uses Java 8)
abstract class TargetJvmVersion8Rule : ComponentMetadataRule {
override fun execute( context: ComponentMetadataContext ) {
context.details.allVariants {
attributes.attribute( TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 8 )
}
}
}

View File

@@ -1111,6 +1111,7 @@ Table.dropCellBackground [lazy] #3c588b HSL 219 40 39 javax.swing.pl
Table.dropCellForeground [lazy] #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI]
Table.dropLineColor [lazy] #6d8ac0 HSL 219 40 59 javax.swing.plaf.ColorUIResource [UI]
Table.dropLineShortColor [lazy] #b4c3df HSL 219 40 79 javax.swing.plaf.ColorUIResource [UI]
Table.editorSelectAllOnStartEditing true
Table.focusCellBackground #46494b HSL 204 3 28 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellForeground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellHighlightBorder [lazy] 2,3,2,3 false com.formdev.flatlaf.ui.FlatTableCellBorder$Focused [UI] lineColor=#6d8ac0 HSL 219 40 59 javax.swing.plaf.ColorUIResource [UI] lineThickness=1.000000
@@ -1119,6 +1120,7 @@ Table.font [active] $defaultFont [UI]
Table.foreground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI]
Table.gridColor #5a5e60 HSL 200 3 36 javax.swing.plaf.ColorUIResource [UI]
Table.intercellSpacing 0,0 javax.swing.plaf.DimensionUIResource [UI]
Table.paintOutsideAlternateRows false
Table.rowHeight 20
Table.scrollPaneBorder [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatScrollPaneBorder [UI]
Table.selectionBackground #4b6eaf HSL 219 40 49 javax.swing.plaf.ColorUIResource [UI]
@@ -1264,7 +1266,6 @@ TitlePane.inactiveBackground #303234 HSL 210 4 20 javax.swing.plaf.Colo
TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8

View File

@@ -1116,6 +1116,7 @@ Table.dropCellBackground [lazy] #3f8fd9 HSL 209 67 55 javax.swing.pl
Table.dropCellForeground [lazy] #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Table.dropLineColor [lazy] #6aa7e1 HSL 209 66 65 javax.swing.plaf.ColorUIResource [UI]
Table.dropLineShortColor [lazy] #15416a HSL 209 67 25 javax.swing.plaf.ColorUIResource [UI]
Table.editorSelectAllOnStartEditing true
Table.focusCellBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellForeground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellHighlightBorder [lazy] 2,3,2,3 false com.formdev.flatlaf.ui.FlatTableCellBorder$Focused [UI] lineColor=#15416a HSL 209 67 25 javax.swing.plaf.ColorUIResource [UI] lineThickness=1.000000
@@ -1124,6 +1125,7 @@ Table.font [active] $defaultFont [UI]
Table.foreground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI]
Table.gridColor #ebebeb HSL 0 0 92 javax.swing.plaf.ColorUIResource [UI]
Table.intercellSpacing 0,0 javax.swing.plaf.DimensionUIResource [UI]
Table.paintOutsideAlternateRows false
Table.rowHeight 20
Table.scrollPaneBorder [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatScrollPaneBorder [UI]
Table.selectionBackground #2675bf HSL 209 67 45 javax.swing.plaf.ColorUIResource [UI]
@@ -1269,7 +1271,6 @@ TitlePane.inactiveBackground #ffffff HSL 0 0 100 javax.swing.plaf.Colo
TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8

View File

@@ -322,7 +322,7 @@ OS Windows 10
#---- javax.swing.JMenuBar ----
1 javax.swing.plaf.basic.LazyActionMap [UI]
takeFocus com.formdev.flatlaf.ui.FlatMenuBarUI$TakeFocus (null)
takeFocus com.formdev.flatlaf.ui.FlatMenuBarUI$TakeFocusAction
#---- javax.swing.JMenuItem ----
@@ -613,7 +613,7 @@ OS Windows 10
selectPreviousRowCell javax.swing.plaf.basic.BasicTableUI$Actions
selectPreviousRowChangeLead javax.swing.plaf.basic.BasicTableUI$Actions
selectPreviousRowExtendSelection javax.swing.plaf.basic.BasicTableUI$Actions
startEditing javax.swing.plaf.basic.BasicTableUI$Actions
startEditing com.formdev.flatlaf.ui.FlatTableUI$StartEditingAction
toggleAndAnchor javax.swing.plaf.basic.BasicTableUI$Actions

View File

@@ -1121,6 +1121,7 @@ Table.dropCellBackground [lazy] #003f99 HSL 215 100 30 javax.swing.pl
Table.dropCellForeground [lazy] #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Table.dropLineColor [lazy] #0069ff HSL 215 100 50 javax.swing.plaf.ColorUIResource [UI]
Table.dropLineShortColor [lazy] #66a5ff HSL 215 100 70 javax.swing.plaf.ColorUIResource [UI]
Table.editorSelectAllOnStartEditing true
Table.focusCellBackground #282828 HSL 0 0 16 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellForeground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellHighlightBorder [lazy] 2,3,2,3 false com.formdev.flatlaf.ui.FlatTableCellBorder$Focused [UI] lineColor=#0069ff HSL 215 100 50 javax.swing.plaf.ColorUIResource [UI] lineThickness=1.000000
@@ -1129,6 +1130,7 @@ Table.font [active] $defaultFont [UI]
Table.foreground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
Table.gridColor #3c3c3c HSL 0 0 24 javax.swing.plaf.ColorUIResource [UI]
Table.intercellSpacing 0,0 javax.swing.plaf.DimensionUIResource [UI]
Table.paintOutsideAlternateRows false
Table.rowHeight 20
Table.scrollPaneBorder [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatScrollPaneBorder [UI]
Table.selectionBackground #0054cc HSL 215 100 40 javax.swing.plaf.ColorUIResource [UI]
@@ -1274,7 +1276,6 @@ TitlePane.inactiveBackground #323232 HSL 0 0 20 javax.swing.plaf.Colo
TitlePane.inactiveForeground #9a9a9a HSL 0 0 60 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8

View File

@@ -1125,6 +1125,7 @@ Table.dropCellBackground [lazy] #1a79ff HSL 215 100 55 javax.swing.pl
Table.dropCellForeground [lazy] #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Table.dropLineColor [lazy] #4d97ff HSL 215 100 65 javax.swing.plaf.ColorUIResource [UI]
Table.dropLineShortColor [lazy] #003580 HSL 215 100 25 javax.swing.plaf.ColorUIResource [UI]
Table.editorSelectAllOnStartEditing true
Table.focusCellBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellForeground #262626 HSL 0 0 15 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellHighlightBorder [lazy] 2,3,2,3 false com.formdev.flatlaf.ui.FlatTableCellBorder$Focused [UI] lineColor=#003580 HSL 215 100 25 javax.swing.plaf.ColorUIResource [UI] lineThickness=1.000000
@@ -1133,6 +1134,7 @@ Table.font [active] $defaultFont [UI]
Table.foreground #262626 HSL 0 0 15 javax.swing.plaf.ColorUIResource [UI]
Table.gridColor #ebebeb HSL 0 0 92 javax.swing.plaf.ColorUIResource [UI]
Table.intercellSpacing 0,0 javax.swing.plaf.DimensionUIResource [UI]
Table.paintOutsideAlternateRows false
Table.rowHeight 20
Table.scrollPaneBorder [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatScrollPaneBorder [UI]
Table.selectionBackground #005fe6 HSL 215 100 45 javax.swing.plaf.ColorUIResource [UI]
@@ -1278,7 +1280,6 @@ TitlePane.inactiveBackground #ececec HSL 0 0 93 javax.swing.plaf.Colo
TitlePane.inactiveForeground #b6b6b6 HSL 0 0 71 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8

View File

@@ -1149,6 +1149,7 @@ Table.dropCellBackground #ff0000 HSL 0 100 50 javax.swing.plaf.Colo
Table.dropCellForeground #00ff00 HSL 120 100 50 javax.swing.plaf.ColorUIResource [UI]
Table.dropLineColor #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Table.dropLineShortColor #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
Table.editorSelectAllOnStartEditing true
Table.focusCellBackground #fffff0 HSL 60 100 97 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellForeground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellHighlightBorder [lazy] 2,3,2,3 false com.formdev.flatlaf.ui.FlatTableCellBorder$Focused [UI] lineColor=#ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI] lineThickness=1.000000
@@ -1157,6 +1158,7 @@ Table.font [active] $defaultFont [UI]
Table.foreground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Table.gridColor #00ffff HSL 180 100 50 javax.swing.plaf.ColorUIResource [UI]
Table.intercellSpacing 0,0 javax.swing.plaf.DimensionUIResource [UI]
Table.paintOutsideAlternateRows false
Table.rowHeight 25
Table.scrollPaneBorder [lazy] 1,9,1,9 false com.formdev.flatlaf.ui.FlatScrollPaneBorder [UI]
Table.selectionBackground #00aa00 HSL 120 100 33 javax.swing.plaf.ColorUIResource [UI]
@@ -1305,7 +1307,6 @@ TitlePane.inactiveBackground #008800 HSL 120 100 27 javax.swing.plaf.Colo
TitlePane.inactiveForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8

View File

@@ -1,5 +1,9 @@
org.pushingpixels.substance.api.skin.SubstanceBusinessLookAndFeel = Substance Business;ctrl F5;org.pushing-pixels:radiance-substance:3.5.1
org.pushingpixels.substance.api.skin.SubstanceGraphiteAquaLookAndFeel = Substance Graphite Aqua;ctrl F6
com.alee.laf.WebLookAndFeel = WebLaf;ctrl F11;com.weblookandfeel:weblaf-ui:1.2.13
org.pushingpixels.radiance.theming.api.skin.RadianceBusinessLookAndFeel = Radiance Business;ctrl F5;org.pushing-pixels:radiance-theming:7.0.1
org.pushingpixels.radiance.theming.api.skin.RadianceGraphiteAquaLookAndFeel = Radiance Graphite Aqua;ctrl F6
com.alee.laf.WebLookAndFeel = WebLaf;ctrl F11;com.weblookandfeel:weblaf-ui:1.2.14
com.jgoodies.looks.plastic.PlasticLookAndFeel = JGoodies Looks Plastic;ctrl F12;com.jgoodies:jgoodies-looks:2.7.0
com.jgoodies.looks.windows.WindowsLookAndFeel = JGoodies Looks Windows;ctrl F9
mdlaf.MaterialLookAndFeel = Material-UI-Swing;shift F11;io.github.vincenzopalazzo:material-ui-swing:1.1.4
com.github.weisj.darklaf.DarkLaf = DarkLaf;shift F12;com.github.weisj:darklaf-core:3.0.2
com.github.weisj.darklaf.theme.laf.DarculaThemeDarklafLookAndFeel = DarkLaf Darcula;ctrl shift F12
com.jtattoo.plaf.smart.SmartLookAndFeel = JTattoo;ctrl shift F11;com.jtattoo:JTattoo:1.6.13

View File

@@ -1,221 +0,0 @@
/*
* Copyright 2019 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.testing;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Function;
import javax.swing.*;
import javax.swing.border.*;
import com.formdev.flatlaf.icons.FlatFileChooserHomeFolderIcon;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
public class FlatChooserTest
extends FlatTestPanel
{
public static void main( String[] args ) {
// Locale.setDefault( Locale.FRENCH );
// Locale.setDefault( Locale.GERMAN );
// Locale.setDefault( Locale.ITALIAN );
// Locale.setDefault( Locale.JAPANESE );
// Locale.setDefault( Locale.SIMPLIFIED_CHINESE );
// Locale.setDefault( Locale.TRADITIONAL_CHINESE );
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, "FlatChooserTest" );
UIManager.put( "FileChooser.shortcuts.filesFunction", (Function<File[], File[]>) files -> {
ArrayList<File> list = new ArrayList<>( Arrays.asList( files ) );
list.add( 0, new File( System.getProperty( "user.home" ) ) );
return list.toArray( new File[list.size()] );
} );
UIManager.put( "FileChooser.shortcuts.displayNameFunction", (Function<File, String>) file -> {
if( file.getAbsolutePath().equals( System.getProperty( "user.home" ) ) )
return "Home";
return null;
} );
UIManager.put( "FileChooser.shortcuts.iconFunction", (Function<File, Icon>) file -> {
if( file.getAbsolutePath().equals( System.getProperty( "user.home" ) ) )
return new FlatFileChooserHomeFolderIcon();
return null;
} );
frame.showFrame( FlatChooserTest::new );
} );
}
FlatChooserTest() {
initComponents();
}
private void showShortcuts() {
UIManager.put( "FileChooser.noPlacesBar", !showShortcutsCheckBox.isSelected() ? true : null );
fileChooser1.updateUI();
}
private void showAccessory() {
JPanel accessory = null;
if( showAccessoryCheckBox.isSelected() ) {
accessory = new JPanel( new BorderLayout() );
accessory.setBackground( Color.green );
accessory.add( new JLabel( " Accessory " ), BorderLayout.CENTER );
}
fileChooser1.setAccessory( accessory );
fileChooser1.revalidate();
fileChooser1.repaint();
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JLabel colorChooserLabel = new JLabel();
JPanel panel2 = new JPanel();
JColorChooser colorChooser1 = new JColorChooser();
JLabel fileChooserLabel = new JLabel();
JPanel panel1 = new JPanel();
fileChooser1 = new JFileChooser();
JPanel panel3 = new JPanel();
showShortcutsCheckBox = new JCheckBox();
showAccessoryCheckBox = new JCheckBox();
JLabel label1 = new JLabel();
JLabel label2 = new JLabel();
JLabel label3 = new JLabel();
JLabel label4 = new JLabel();
JLabel label5 = new JLabel();
JLabel label6 = new JLabel();
JLabel label7 = new JLabel();
JLabel label8 = new JLabel();
JLabel label9 = new JLabel();
JLabel label10 = new JLabel();
JLabel label11 = new JLabel();
//======== this ========
setLayout(new MigLayout(
"ltr,insets dialog,hidemode 3",
// columns
"[]" +
"[grow]",
// rows
"[top]" +
"[grow,fill]" +
"[]" +
"[]"));
//---- colorChooserLabel ----
colorChooserLabel.setText("JColorChooser:");
add(colorChooserLabel, "cell 0 0");
//======== panel2 ========
{
panel2.setBorder(new MatteBorder(4, 4, 4, 4, Color.red));
panel2.setLayout(new BorderLayout());
panel2.add(colorChooser1, BorderLayout.CENTER);
}
add(panel2, "cell 1 0");
//---- fileChooserLabel ----
fileChooserLabel.setText("JFileChooser:");
add(fileChooserLabel, "cell 0 1,aligny top,growy 0");
//======== panel1 ========
{
panel1.setBorder(new MatteBorder(4, 4, 4, 4, Color.red));
panel1.setLayout(new BorderLayout());
panel1.add(fileChooser1, BorderLayout.CENTER);
}
add(panel1, "cell 1 1,growx");
//======== panel3 ========
{
panel3.setLayout(new MigLayout(
"hidemode 3",
// columns
"[fill]" +
"[fill]",
// rows
"[]"));
//---- showShortcutsCheckBox ----
showShortcutsCheckBox.setText("Show Shortcuts");
showShortcutsCheckBox.setSelected(true);
showShortcutsCheckBox.addActionListener(e -> showShortcuts());
panel3.add(showShortcutsCheckBox, "cell 0 0");
//---- showAccessoryCheckBox ----
showAccessoryCheckBox.setText("Show Accessory");
showAccessoryCheckBox.addActionListener(e -> showAccessory());
panel3.add(showAccessoryCheckBox, "cell 1 0");
}
add(panel3, "cell 1 2");
//---- label1 ----
label1.setText("icons:");
add(label1, "cell 0 3");
//---- label2 ----
label2.setIcon(UIManager.getIcon("FileView.directoryIcon"));
add(label2, "cell 1 3");
//---- label3 ----
label3.setIcon(UIManager.getIcon("FileView.fileIcon"));
add(label3, "cell 1 3");
//---- label4 ----
label4.setIcon(UIManager.getIcon("FileView.computerIcon"));
add(label4, "cell 1 3");
//---- label5 ----
label5.setIcon(UIManager.getIcon("FileView.hardDriveIcon"));
add(label5, "cell 1 3");
//---- label6 ----
label6.setIcon(UIManager.getIcon("FileView.floppyDriveIcon"));
add(label6, "cell 1 3");
//---- label7 ----
label7.setIcon(UIManager.getIcon("FileChooser.newFolderIcon"));
add(label7, "cell 1 3");
//---- label8 ----
label8.setIcon(UIManager.getIcon("FileChooser.upFolderIcon"));
add(label8, "cell 1 3");
//---- label9 ----
label9.setIcon(UIManager.getIcon("FileChooser.homeFolderIcon"));
add(label9, "cell 1 3");
//---- label10 ----
label10.setIcon(UIManager.getIcon("FileChooser.detailsViewIcon"));
add(label10, "cell 1 3");
//---- label11 ----
label11.setIcon(UIManager.getIcon("FileChooser.listViewIcon"));
add(label11, "cell 1 3");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JFileChooser fileChooser1;
private JCheckBox showShortcutsCheckBox;
private JCheckBox showAccessoryCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -1,153 +0,0 @@
JFDML JFormDesigner: "8.0.0.0.194" Java: "17.0.2" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
auxiliary() {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[][grow]"
"$rowConstraints": "[top][grow,fill][][]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "colorChooserLabel"
"text": "JColorChooser:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel2"
"border": new javax.swing.border.MatteBorder( 4, 4, 4, 4, sfield java.awt.Color red )
add( new FormComponent( "javax.swing.JColorChooser" ) {
name: "colorChooser1"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fileChooserLabel"
"text": "JFileChooser:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,aligny top,growy 0"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel1"
"border": new javax.swing.border.MatteBorder( 4, 4, 4, 4, sfield java.awt.Color red )
add( new FormComponent( "javax.swing.JFileChooser" ) {
name: "fileChooser1"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1,growx"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill][fill]"
"$rowConstraints": "[]"
} ) {
name: "panel3"
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showShortcutsCheckBox"
"text": "Show Shortcuts"
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showShortcuts", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showAccessoryCheckBox"
"text": "Show Accessory"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showAccessory", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
"text": "icons:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label2"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.directoryIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.fileIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label4"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.computerIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label5"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.hardDriveIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.floppyDriveIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label7"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.newFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label8"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.upFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label9"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.homeFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label10"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.detailsViewIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label11"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.listViewIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 790, 790 )
} )
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2019 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.testing;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
public class FlatColorChooserTest
extends FlatTestPanel
{
public static void main( String[] args ) {
// Locale.setDefault( Locale.FRENCH );
// Locale.setDefault( Locale.GERMAN );
// Locale.setDefault( Locale.ITALIAN );
// Locale.setDefault( Locale.JAPANESE );
// Locale.setDefault( Locale.SIMPLIFIED_CHINESE );
// Locale.setDefault( Locale.TRADITIONAL_CHINESE );
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, "FlatColorChooserTest" );
frame.showFrame( FlatColorChooserTest::new );
} );
}
FlatColorChooserTest() {
initComponents();
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JLabel colorChooserLabel = new JLabel();
JPanel panel2 = new JPanel();
JColorChooser colorChooser1 = new JColorChooser();
//======== this ========
setLayout(new MigLayout(
"ltr,insets dialog,hidemode 3",
// columns
"[]" +
"[grow]",
// rows
"[top]"));
//---- colorChooserLabel ----
colorChooserLabel.setText("JColorChooser:");
add(colorChooserLabel, "cell 0 0");
//======== panel2 ========
{
panel2.setBorder(new MatteBorder(4, 4, 4, 4, Color.red));
panel2.setLayout(new BorderLayout());
panel2.add(colorChooser1, BorderLayout.CENTER);
}
add(panel2, "cell 1 0");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -0,0 +1,37 @@
JFDML JFormDesigner: "8.2.0.0.331" Java: "21" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
auxiliary() {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[][grow]"
"$rowConstraints": "[top]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "colorChooserLabel"
"text": "JColorChooser:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel2"
"border": new javax.swing.border.MatteBorder( 4, 4, 4, 4, sfield java.awt.Color red )
add( new FormComponent( "javax.swing.JColorChooser" ) {
name: "colorChooser1"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 790, 790 )
} )
}
}

View File

@@ -24,6 +24,7 @@ import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.Random;
import javax.swing.*;
import javax.swing.border.*;
import com.formdev.flatlaf.FlatLaf;
@@ -178,6 +179,20 @@ public class FlatContainerTest
tabbedPane.addTab( "Tab 4", new JLabel( "non-opaque content", SwingConstants.CENTER ) );
break;
case 4:
tabbedPane.addTab( "Tab 5", new JLabel( "random background content", SwingConstants.CENTER ) {
Random random = new Random();
@Override
protected void paintComponent( Graphics g ) {
g.setColor( new Color( random.nextInt() ) );
g.fillRect( 0, 0, getWidth(), getHeight() );
super.paintComponent( g );
}
} );
break;
default:
int index = tabbedPane.getTabCount() + 1;
tabbedPane.addTab( "Tab " + index, createTab( "tab content " + index ) );

View File

@@ -0,0 +1,556 @@
/*
* Copyright 2019 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.testing;
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.function.Function;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.filechooser.FileSystemView;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatFileChooserHomeFolderIcon;
import com.formdev.flatlaf.ui.JavaCompatibility2;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
public class FlatFileChooserTest
extends FlatTestPanel
{
public static void main( String[] args ) {
// Locale.setDefault( Locale.FRENCH );
// Locale.setDefault( Locale.GERMAN );
// Locale.setDefault( Locale.ITALIAN );
// Locale.setDefault( Locale.JAPANESE );
// Locale.setDefault( Locale.SIMPLIFIED_CHINESE );
// Locale.setDefault( Locale.TRADITIONAL_CHINESE );
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, "FlatFileChooserTest" );
UIManager.put( "FileChooser.shortcuts.filesFunction", (Function<File[], File[]>) files -> {
ArrayList<File> list = new ArrayList<>( Arrays.asList( files ) );
list.add( 0, new File( System.getProperty( "user.home" ) ) );
return list.toArray( new File[list.size()] );
} );
UIManager.put( "FileChooser.shortcuts.displayNameFunction", (Function<File, String>) file -> {
if( file.getAbsolutePath().equals( System.getProperty( "user.home" ) ) )
return "Home";
return null;
} );
UIManager.put( "FileChooser.shortcuts.iconFunction", (Function<File, Icon>) file -> {
if( file.getAbsolutePath().equals( System.getProperty( "user.home" ) ) )
return new FlatFileChooserHomeFolderIcon();
return null;
} );
frame.showFrame( FlatFileChooserTest::new );
} );
}
FlatFileChooserTest() {
initComponents();
dialogTypeField.init( DialogType.class, false );
fileSelectionModeField.init( FileSelectionMode.class, false );
localesField.init( Locales.class, false );
showControlButtonsCheckBox.setSelected( fileChooser1.getControlButtonsAreShown() );
multiSelectionCheckBox.setSelected( fileChooser1.isMultiSelectionEnabled() );
fileHidingCheckBox.setSelected( fileChooser1.isFileHidingEnabled() );
dragCheckBox.setSelected( fileChooser1.getDragEnabled() );
filterAllFilesCheckBox.setSelected( fileChooser1.isAcceptAllFileFilterUsed() );
updateOutput();
}
private void fileChooser1PropertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case JFileChooser.DIRECTORY_CHANGED_PROPERTY:
case JFileChooser.SELECTED_FILE_CHANGED_PROPERTY:
case JFileChooser.SELECTED_FILES_CHANGED_PROPERTY:
updateOutput();
break;
}
}
private void updateOutput() {
currentDirectoryField.setText( String.valueOf( fileChooser1.getCurrentDirectory() ) );
selectedFileField.setText( String.valueOf( fileChooser1.getSelectedFile() ) );
selectedFilesField.setText( Arrays.toString( fileChooser1.getSelectedFiles() ) );
}
private void dialogTypeChanged() {
DialogType value = dialogTypeField.getSelectedValue();
int dialogType = (value != null) ? value.value : JFileChooser.OPEN_DIALOG;
if( dialogType == JFileChooser.CUSTOM_DIALOG )
fileChooser1.setApproveButtonText( "Custom" );
fileChooser1.setDialogType( dialogType );
}
private void fileSelectionModeChanged() {
FileSelectionMode value = fileSelectionModeField.getSelectedValue();
int mode = (value != null) ? value.value : JFileChooser.FILES_ONLY;
fileChooser1.setFileSelectionMode( mode );
}
private void showControlButtons() {
fileChooser1.setControlButtonsAreShown( showControlButtonsCheckBox.isSelected() );
fileChooser1.revalidate();
fileChooser1.repaint();
}
private void showShortcuts() {
UIManager.put( "FileChooser.noPlacesBar", !showShortcutsCheckBox.isSelected() ? true : null );
fileChooser1.updateUI();
}
private void showAccessory() {
JPanel accessory = null;
if( showAccessoryCheckBox.isSelected() ) {
accessory = new JPanel( new BorderLayout() );
accessory.setBackground( Color.green );
accessory.add( new JLabel( " Accessory " ), BorderLayout.CENTER );
}
fileChooser1.setAccessory( accessory );
fileChooser1.revalidate();
fileChooser1.repaint();
}
private void multiSelection() {
fileChooser1.setMultiSelectionEnabled( multiSelectionCheckBox.isSelected() );
}
private void fileHiding() {
fileChooser1.setFileHidingEnabled( fileHidingCheckBox.isSelected() );
}
private void drag() {
fileChooser1.setDragEnabled( dragCheckBox.isSelected() );
}
private final FileFilter TEXT_FILTER = new FileNameExtensionFilter( "Text Files", "txt", "md" );
private final FileFilter IMAGES_FILTER = new FileNameExtensionFilter( "Images", "png", "git", "jpg", "jpeg" );
private final FileFilter LONG_DESC_FILTER = new FileNameExtensionFilter( "Some long description (.abc, .def, .ghi, .jkl)", "dummy" );
private final FileFilter EXTRA_LONG_DESC_FILTER = new FileNameExtensionFilter( "Some super extra long description (.abc, .def, .ghi, .jkl, .mno, .pqr, .stu)", "dummy" );
private void filterChanged() {
boolean all = filterAllFilesCheckBox.isSelected();
if( all != fileChooser1.isAcceptAllFileFilterUsed() )
fileChooser1.setAcceptAllFileFilterUsed( all );
addRemoveFilter( filterTextFilesCheckBox.isSelected(), TEXT_FILTER );
addRemoveFilter( filterImagesCheckBox.isSelected(), IMAGES_FILTER );
addRemoveFilter( filterLongDescCheckBox.isSelected(), LONG_DESC_FILTER );
addRemoveFilter( filterExtraLongDescCheckBox.isSelected(), EXTRA_LONG_DESC_FILTER );
}
private void addRemoveFilter( boolean add, FileFilter filter ) {
if( add )
fileChooser1.addChoosableFileFilter( filter );
else
fileChooser1.removeChoosableFileFilter( filter );
}
private void localesChanged() {
Locales value = localesField.getSelectedValue();
Locale locale = (value != null) ? value.value : Locale.ENGLISH;
SwingUtilities.invokeLater( () -> {
Locale.setDefault( locale );
JComponent.setDefaultLocale( locale );
fileChooser1.setLocale( locale );
ResourceBundle.clearCache();
try {
UIManager.setLookAndFeel( UIManager.getLookAndFeel().getClass().getName() );
FlatLaf.updateUI();
} catch( Exception ex ) {
ex.printStackTrace();
}
} );
}
private void printShortcutFiles() {
printFiles( JavaCompatibility2.getChooserShortcutPanelFiles( fileChooser1.getFileSystemView() ) );
}
private void printComboBoxFiles() {
printFiles( JavaCompatibility2.getChooserComboBoxFiles( fileChooser1.getFileSystemView() ) );
}
private void printRoots() {
FileSystemView fsv = fileChooser1.getFileSystemView();
File[] roots = fsv.getRoots();
printFiles( roots );
for( File root : roots )
printFiles( fsv.getFiles( root, true ) );
}
private void printFiles( File[] files ) {
System.out.println( "--------------------------------" );
FileSystemView fsv = fileChooser1.getFileSystemView();
for( File file : files ) {
System.out.printf( "%-30s ", file );
System.out.println(
(fsv.isComputerNode( file ) ? "computer " : "") +
(fsv.isDrive( file ) ? "drive " : "") +
(fsv.isFileSystem( file ) ? "fileSystem " : "") +
(fsv.isFileSystemRoot( file ) ? "fileSystemRoot " : "") +
(fsv.isFloppyDrive( file ) ? "floppyDrive " : "") +
(fsv.isHiddenFile( file ) ? "hiddenFile " : "") +
(fsv.isRoot( file ) ? "root " : "") +
(fsv.isTraversable( file ) ? "traversable " : "") );
}
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JLabel fileChooserLabel = new JLabel();
JPanel panel1 = new JPanel();
fileChooser1 = new JFileChooser();
JLabel currentDirectoryLabel = new JLabel();
currentDirectoryField = new JTextField();
JLabel selectedFileLabel = new JLabel();
selectedFileField = new JTextField();
JLabel selectedFilesLabel = new JLabel();
selectedFilesField = new JTextField();
JLabel dialogTypeLabel = new JLabel();
dialogTypeField = new FlatTestEnumSelector<>();
showControlButtonsCheckBox = new JCheckBox();
showShortcutsCheckBox = new JCheckBox();
showAccessoryCheckBox = new JCheckBox();
JLabel fileSelectionModeLabel = new JLabel();
fileSelectionModeField = new FlatTestEnumSelector<>();
multiSelectionCheckBox = new JCheckBox();
fileHidingCheckBox = new JCheckBox();
dragCheckBox = new JCheckBox();
JLabel filtersLabel = new JLabel();
filterAllFilesCheckBox = new JCheckBox();
filterTextFilesCheckBox = new JCheckBox();
filterImagesCheckBox = new JCheckBox();
filterLongDescCheckBox = new JCheckBox();
filterExtraLongDescCheckBox = new JCheckBox();
printShortcutFilesButton = new JButton();
printComboBoxFilesButton = new JButton();
printRootsButton = new JButton();
JLabel localesLabel = new JLabel();
localesField = new FlatTestEnumSelector<>();
JLabel label1 = new JLabel();
JLabel label2 = new JLabel();
JLabel label3 = new JLabel();
JLabel label4 = new JLabel();
JLabel label5 = new JLabel();
JLabel label6 = new JLabel();
JLabel label7 = new JLabel();
JLabel label8 = new JLabel();
JLabel label9 = new JLabel();
JLabel label10 = new JLabel();
JLabel label11 = new JLabel();
//======== this ========
setLayout(new MigLayout(
"ltr,insets dialog,hidemode 3",
// columns
"[]" +
"[]" +
"[grow]",
// rows
"[grow,fill]" +
"[]" +
"[]" +
"[]" +
"[]" +
"[]" +
"[]" +
"[]" +
"[]para" +
"[]"));
//---- fileChooserLabel ----
fileChooserLabel.setText("JFileChooser:");
add(fileChooserLabel, "cell 0 0,aligny top,growy 0");
//======== panel1 ========
{
panel1.setBorder(new MatteBorder(4, 4, 4, 4, Color.red));
panel1.setLayout(new BorderLayout());
//---- fileChooser1 ----
fileChooser1.addPropertyChangeListener(e -> fileChooser1PropertyChange(e));
panel1.add(fileChooser1, BorderLayout.CENTER);
}
add(panel1, "cell 1 0 2 1,growx");
//---- currentDirectoryLabel ----
currentDirectoryLabel.setText("Current Directory:");
add(currentDirectoryLabel, "cell 0 1");
//---- currentDirectoryField ----
currentDirectoryField.setEditable(false);
add(currentDirectoryField, "cell 1 1 2 1,growx");
//---- selectedFileLabel ----
selectedFileLabel.setText("Selected File:");
add(selectedFileLabel, "cell 0 2");
//---- selectedFileField ----
selectedFileField.setEditable(false);
add(selectedFileField, "cell 1 2 2 1,growx");
//---- selectedFilesLabel ----
selectedFilesLabel.setText("Selected Files:");
add(selectedFilesLabel, "cell 0 3");
//---- selectedFilesField ----
selectedFilesField.setEditable(false);
add(selectedFilesField, "cell 1 3 2 1,growx");
//---- dialogTypeLabel ----
dialogTypeLabel.setText("Dialog Type:");
add(dialogTypeLabel, "cell 0 4");
//---- dialogTypeField ----
dialogTypeField.addActionListener(e -> dialogTypeChanged());
add(dialogTypeField, "cell 1 4");
//---- showControlButtonsCheckBox ----
showControlButtonsCheckBox.setText("Show Control Buttons");
showControlButtonsCheckBox.addActionListener(e -> showControlButtons());
add(showControlButtonsCheckBox, "cell 2 4");
//---- showShortcutsCheckBox ----
showShortcutsCheckBox.setText("Show Shortcuts");
showShortcutsCheckBox.setSelected(true);
showShortcutsCheckBox.addActionListener(e -> showShortcuts());
add(showShortcutsCheckBox, "cell 2 4");
//---- showAccessoryCheckBox ----
showAccessoryCheckBox.setText("Show Accessory");
showAccessoryCheckBox.addActionListener(e -> showAccessory());
add(showAccessoryCheckBox, "cell 2 4");
//---- fileSelectionModeLabel ----
fileSelectionModeLabel.setText("File Selection Mode:");
add(fileSelectionModeLabel, "cell 0 5");
//---- fileSelectionModeField ----
fileSelectionModeField.addActionListener(e -> fileSelectionModeChanged());
add(fileSelectionModeField, "cell 1 5");
//---- multiSelectionCheckBox ----
multiSelectionCheckBox.setText("Multi Selection");
multiSelectionCheckBox.addActionListener(e -> multiSelection());
add(multiSelectionCheckBox, "cell 2 5");
//---- fileHidingCheckBox ----
fileHidingCheckBox.setText("File Hiding");
fileHidingCheckBox.addActionListener(e -> fileHiding());
add(fileHidingCheckBox, "cell 2 5");
//---- dragCheckBox ----
dragCheckBox.setText("Drag");
dragCheckBox.addActionListener(e -> drag());
add(dragCheckBox, "cell 2 5");
//---- filtersLabel ----
filtersLabel.setText("Filters:");
add(filtersLabel, "cell 0 6");
//---- filterAllFilesCheckBox ----
filterAllFilesCheckBox.setText("All Files");
filterAllFilesCheckBox.addActionListener(e -> filterChanged());
add(filterAllFilesCheckBox, "cell 1 6 2 1");
//---- filterTextFilesCheckBox ----
filterTextFilesCheckBox.setText("Text Files");
filterTextFilesCheckBox.addActionListener(e -> filterChanged());
add(filterTextFilesCheckBox, "cell 1 6 2 1");
//---- filterImagesCheckBox ----
filterImagesCheckBox.setText("Images");
filterImagesCheckBox.addActionListener(e -> filterChanged());
add(filterImagesCheckBox, "cell 1 6 2 1");
//---- filterLongDescCheckBox ----
filterLongDescCheckBox.setText("Long description");
filterLongDescCheckBox.addActionListener(e -> filterChanged());
add(filterLongDescCheckBox, "cell 1 6 2 1");
//---- filterExtraLongDescCheckBox ----
filterExtraLongDescCheckBox.setText("Extra Long description");
filterExtraLongDescCheckBox.addActionListener(e -> filterChanged());
add(filterExtraLongDescCheckBox, "cell 1 6 2 1");
//---- printShortcutFilesButton ----
printShortcutFilesButton.setText("Print Shortcut Files");
printShortcutFilesButton.addActionListener(e -> printShortcutFiles());
add(printShortcutFilesButton, "cell 1 7 2 1");
//---- printComboBoxFilesButton ----
printComboBoxFilesButton.setText("Print ComboBox Files");
printComboBoxFilesButton.addActionListener(e -> printComboBoxFiles());
add(printComboBoxFilesButton, "cell 1 7 2 1");
//---- printRootsButton ----
printRootsButton.setText("Print Roots");
printRootsButton.addActionListener(e -> printRoots());
add(printRootsButton, "cell 1 7 2 1");
//---- localesLabel ----
localesLabel.setText("Locales:");
add(localesLabel, "cell 0 8");
//---- localesField ----
localesField.addActionListener(e -> localesChanged());
add(localesField, "cell 1 8 2 1");
//---- label1 ----
label1.setText("icons:");
add(label1, "cell 0 9");
//---- label2 ----
label2.setIcon(UIManager.getIcon("FileView.directoryIcon"));
add(label2, "cell 1 9 2 1");
//---- label3 ----
label3.setIcon(UIManager.getIcon("FileView.fileIcon"));
add(label3, "cell 1 9 2 1");
//---- label4 ----
label4.setIcon(UIManager.getIcon("FileView.computerIcon"));
add(label4, "cell 1 9 2 1");
//---- label5 ----
label5.setIcon(UIManager.getIcon("FileView.hardDriveIcon"));
add(label5, "cell 1 9 2 1");
//---- label6 ----
label6.setIcon(UIManager.getIcon("FileView.floppyDriveIcon"));
add(label6, "cell 1 9 2 1");
//---- label7 ----
label7.setIcon(UIManager.getIcon("FileChooser.newFolderIcon"));
add(label7, "cell 1 9 2 1");
//---- label8 ----
label8.setIcon(UIManager.getIcon("FileChooser.upFolderIcon"));
add(label8, "cell 1 9 2 1");
//---- label9 ----
label9.setIcon(UIManager.getIcon("FileChooser.homeFolderIcon"));
add(label9, "cell 1 9 2 1");
//---- label10 ----
label10.setIcon(UIManager.getIcon("FileChooser.detailsViewIcon"));
add(label10, "cell 1 9 2 1");
//---- label11 ----
label11.setIcon(UIManager.getIcon("FileChooser.listViewIcon"));
add(label11, "cell 1 9 2 1");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JFileChooser fileChooser1;
private JTextField currentDirectoryField;
private JTextField selectedFileField;
private JTextField selectedFilesField;
private FlatTestEnumSelector<DialogType> dialogTypeField;
private JCheckBox showControlButtonsCheckBox;
private JCheckBox showShortcutsCheckBox;
private JCheckBox showAccessoryCheckBox;
private FlatTestEnumSelector<FileSelectionMode> fileSelectionModeField;
private JCheckBox multiSelectionCheckBox;
private JCheckBox fileHidingCheckBox;
private JCheckBox dragCheckBox;
private JCheckBox filterAllFilesCheckBox;
private JCheckBox filterTextFilesCheckBox;
private JCheckBox filterImagesCheckBox;
private JCheckBox filterLongDescCheckBox;
private JCheckBox filterExtraLongDescCheckBox;
private JButton printShortcutFilesButton;
private JButton printComboBoxFilesButton;
private JButton printRootsButton;
private FlatTestEnumSelector<Locales> localesField;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- enum DialogType ----------------------------------------------------
enum DialogType {
open( JFileChooser.OPEN_DIALOG ),
save( JFileChooser.SAVE_DIALOG ),
custom( JFileChooser.CUSTOM_DIALOG );
public final int value;
DialogType( int value ) {
this.value = value;
}
}
//---- enum FileSelectionMode ---------------------------------------------
enum FileSelectionMode {
files_only( JFileChooser.FILES_ONLY ),
directories_only( JFileChooser.DIRECTORIES_ONLY ),
files_and_directories( JFileChooser.FILES_AND_DIRECTORIES );
public final int value;
FileSelectionMode( int value ) {
this.value = value;
}
}
//---- enum Locales -------------------------------------------------------
// locales supported by Swing
// (see https://github.com/openjdk/jdk/tree/master/src/java.desktop/share/classes/com/sun/swing/internal/plaf/metal/resources)
enum Locales {
english( Locale.ENGLISH ),
german( Locale.GERMAN ),
spanish( new Locale( "es" ) ),
french( Locale.FRENCH ),
italian( Locale.ITALIAN ),
japanese( Locale.JAPANESE ),
korean( Locale.KOREAN ),
brazilian_portuguese( new Locale( "pt", "BR" ) ),
swedish( new Locale( "sv" ) ),
simplified_chinese( Locale.SIMPLIFIED_CHINESE ),
traditional_chinese( Locale.TRADITIONAL_CHINESE );
public final Locale value;
Locales( Locale value ) {
this.value = value;
}
}
}

View File

@@ -0,0 +1,347 @@
JFDML JFormDesigner: "8.2.0.0.331" Java: "21" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
auxiliary() {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[][][grow]"
"$rowConstraints": "[grow,fill][][][][][][][][]para[]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fileChooserLabel"
"text": "JFileChooser:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0,aligny top,growy 0"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel1"
"border": new javax.swing.border.MatteBorder( 4, 4, 4, 4, sfield java.awt.Color red )
add( new FormComponent( "javax.swing.JFileChooser" ) {
name: "fileChooser1"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.beans.PropertyChangeListener", "propertyChange", "fileChooser1PropertyChange", true ) )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0 2 1,growx"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "currentDirectoryLabel"
"text": "Current Directory:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "currentDirectoryField"
"editable": false
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1 2 1,growx"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "selectedFileLabel"
"text": "Selected File:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "selectedFileField"
"editable": false
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2 2 1,growx"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "selectedFilesLabel"
"text": "Selected Files:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "selectedFilesField"
"editable": false
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3 2 1,growx"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "dialogTypeLabel"
"text": "Dialog Type:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatTestEnumSelector" ) {
name: "dialogTypeField"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.typeParameters": "DialogType"
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dialogTypeChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 4"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showControlButtonsCheckBox"
"text": "Show Control Buttons"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showControlButtons", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 4"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showShortcutsCheckBox"
"text": "Show Shortcuts"
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showShortcuts", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 4"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showAccessoryCheckBox"
"text": "Show Accessory"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showAccessory", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 4"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fileSelectionModeLabel"
"text": "File Selection Mode:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatTestEnumSelector" ) {
name: "fileSelectionModeField"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.typeParameters": "FileSelectionMode"
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "fileSelectionModeChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "multiSelectionCheckBox"
"text": "Multi Selection"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "multiSelection", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 5"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "fileHidingCheckBox"
"text": "File Hiding"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "fileHiding", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 5"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "dragCheckBox"
"text": "Drag"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "drag", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "filtersLabel"
"text": "Filters:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "filterAllFilesCheckBox"
"text": "All Files"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "filterChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6 2 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "filterTextFilesCheckBox"
"text": "Text Files"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "filterChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6 2 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "filterImagesCheckBox"
"text": "Images"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "filterChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6 2 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "filterLongDescCheckBox"
"text": "Long description"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "filterChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6 2 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "filterExtraLongDescCheckBox"
"text": "Extra Long description"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "filterChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6 2 1"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "printShortcutFilesButton"
"text": "Print Shortcut Files"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "printShortcutFiles", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 7 2 1"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "printComboBoxFilesButton"
"text": "Print ComboBox Files"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "printComboBoxFiles", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 7 2 1"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "printRootsButton"
"text": "Print Roots"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "printRoots", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 7 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "localesLabel"
"text": "Locales:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 8"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatTestEnumSelector" ) {
name: "localesField"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.typeParameters": "Locales"
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "localesChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 8 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
"text": "icons:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label2"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.directoryIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.fileIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label4"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.computerIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label5"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.hardDriveIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.floppyDriveIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label7"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.newFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label8"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.upFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label9"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.homeFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label10"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.detailsViewIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label11"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.listViewIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 715, 660 )
} )
}
}

View File

@@ -0,0 +1,291 @@
/*
* 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.testing;
import java.awt.*;
import javax.swing.*;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatNativeMacLibrary;
import com.formdev.flatlaf.util.SystemInfo;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
public class FlatMacOSTest
extends FlatTestPanel
{
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, FlatMacOSTest.class.getSimpleName() );
frame.applyComponentOrientationToFrame = true;
JRootPane rootPane = frame.getRootPane();
rootPane.putClientProperty( "apple.awt.fullWindowContent", true );
rootPane.putClientProperty( "apple.awt.transparentTitleBar", true );
rootPane.putClientProperty( "apple.awt.windowTitleVisible", false );
frame.showFrame( FlatMacOSTest::new );
} );
}
FlatMacOSTest() {
initComponents();
if( SystemInfo.isMacFullWindowContentSupported ) {
fullWindowContentHint.setVisible( false );
transparentTitleBarHint.setVisible( false );
}
if( SystemInfo.isJava_17_orLater ) {
windowTitleVisibleHint.setVisible( false );
buttonsSpacingHint.setVisible( false );
}
placeholderPanel.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac zeroInFullScreen" );
UIManager.put( "FlatLaf.debug.panel.showPlaceholders", true );
}
@Override
public void addNotify() {
super.addNotify();
JRootPane rootPane = getRootPane();
fullWindowContentCheckBox.setSelected( FlatClientProperties.clientPropertyBoolean( rootPane, "apple.awt.fullWindowContent", false ) );
transparentTitleBarCheckBox.setSelected( FlatClientProperties.clientPropertyBoolean( rootPane, "apple.awt.transparentTitleBar", false ) );
windowTitleVisibleCheckBox.setSelected( FlatClientProperties.clientPropertyBoolean( rootPane, "apple.awt.windowTitleVisible", true ) );
rootPane.addPropertyChangeListener( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS, e -> {
Rectangle bounds = (Rectangle) e.getNewValue();
fullWindowContentButtonsBoundsField.setText( bounds2string( bounds ) );
} );
updateNativeButtonBounds();
}
@Override
public void updateUI() {
super.updateUI();
if( nativeButtonsBoundsField != null )
updateNativeButtonBounds();
}
private void fullWindowContentChanged() {
getRootPane().putClientProperty( "apple.awt.fullWindowContent", fullWindowContentCheckBox.isSelected() );
}
private void transparentTitleBarChanged() {
getRootPane().putClientProperty( "apple.awt.transparentTitleBar", transparentTitleBarCheckBox.isSelected() );
}
private void windowTitleVisibleChanged() {
getRootPane().putClientProperty( "apple.awt.windowTitleVisible", windowTitleVisibleCheckBox.isSelected() );
}
private void buttonsSpacingChanged() {
String buttonsSpacing = null;
if( buttonsSpacingMediumRadioButton.isSelected() )
buttonsSpacing = FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_MEDIUM;
else if( buttonsSpacingLargeRadioButton.isSelected() )
buttonsSpacing = FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_LARGE;
getRootPane().putClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING, buttonsSpacing );
updateNativeButtonBounds();
}
private void updateNativeButtonBounds() {
if( !FlatNativeMacLibrary.isLoaded() )
return;
Window window = SwingUtilities.windowForComponent( this );
Rectangle bounds = FlatNativeMacLibrary.getWindowButtonsBounds( window );
nativeButtonsBoundsField.setText( bounds2string( bounds ) );
}
private String bounds2string( Rectangle bounds ) {
return (bounds != null)
? bounds.width + ", " + bounds.height + " @ " + bounds.x + ", " + bounds.y
: "null";
}
private void toggleFullScreen() {
if( !FlatNativeMacLibrary.isLoaded() )
return;
Window window = SwingUtilities.windowForComponent( this );
FlatNativeMacLibrary.toggleWindowFullScreen( window );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JPanel panel1 = new JPanel();
placeholderPanel = new JPanel();
JPanel panel2 = new JPanel();
fullWindowContentCheckBox = new JCheckBox();
fullWindowContentHint = new JLabel();
transparentTitleBarCheckBox = new JCheckBox();
transparentTitleBarHint = new JLabel();
windowTitleVisibleCheckBox = new JCheckBox();
windowTitleVisibleHint = new JLabel();
JLabel buttonsSpacingLabel = new JLabel();
buttonsSpacingDefaultRadioButton = new JRadioButton();
buttonsSpacingMediumRadioButton = new JRadioButton();
buttonsSpacingLargeRadioButton = new JRadioButton();
buttonsSpacingHint = new JLabel();
JLabel fullWindowContentButtonsBoundsLabel = new JLabel();
fullWindowContentButtonsBoundsField = new JLabel();
JLabel nativeButtonsBoundsLabel = new JLabel();
nativeButtonsBoundsField = new JLabel();
JButton toggleFullScreenButton = new JButton();
//======== this ========
setLayout(new BorderLayout());
//======== panel1 ========
{
panel1.setLayout(new BorderLayout());
//======== placeholderPanel ========
{
placeholderPanel.setBackground(Color.green);
placeholderPanel.setLayout(new FlowLayout());
}
panel1.add(placeholderPanel, BorderLayout.WEST);
}
add(panel1, BorderLayout.PAGE_START);
//======== panel2 ========
{
panel2.setLayout(new MigLayout(
"ltr,insets dialog,hidemode 3",
// columns
"[left]" +
"[left]" +
"[left]" +
"[left]para" +
"[fill]",
// rows
"[]" +
"[]" +
"[]" +
"[fill]" +
"[]" +
"[]para" +
"[]"));
//---- fullWindowContentCheckBox ----
fullWindowContentCheckBox.setText("fullWindowContent");
fullWindowContentCheckBox.addActionListener(e -> fullWindowContentChanged());
panel2.add(fullWindowContentCheckBox, "cell 0 0");
//---- fullWindowContentHint ----
fullWindowContentHint.setText("requires Java 12, 11.0.8 or 8u292");
fullWindowContentHint.setForeground(Color.red);
panel2.add(fullWindowContentHint, "cell 4 0");
//---- transparentTitleBarCheckBox ----
transparentTitleBarCheckBox.setText("transparentTitleBar");
transparentTitleBarCheckBox.addActionListener(e -> transparentTitleBarChanged());
panel2.add(transparentTitleBarCheckBox, "cell 0 1");
//---- transparentTitleBarHint ----
transparentTitleBarHint.setText("requires Java 12, 11.0.8 or 8u292");
transparentTitleBarHint.setForeground(Color.red);
panel2.add(transparentTitleBarHint, "cell 4 1");
//---- windowTitleVisibleCheckBox ----
windowTitleVisibleCheckBox.setText("windowTitleVisible");
windowTitleVisibleCheckBox.addActionListener(e -> windowTitleVisibleChanged());
panel2.add(windowTitleVisibleCheckBox, "cell 0 2");
//---- windowTitleVisibleHint ----
windowTitleVisibleHint.setText("requires Java 17");
windowTitleVisibleHint.setForeground(Color.red);
panel2.add(windowTitleVisibleHint, "cell 4 2");
//---- buttonsSpacingLabel ----
buttonsSpacingLabel.setText("Buttons spacing:");
panel2.add(buttonsSpacingLabel, "cell 0 3");
//---- buttonsSpacingDefaultRadioButton ----
buttonsSpacingDefaultRadioButton.setText("Default");
buttonsSpacingDefaultRadioButton.setSelected(true);
buttonsSpacingDefaultRadioButton.addActionListener(e -> buttonsSpacingChanged());
panel2.add(buttonsSpacingDefaultRadioButton, "cell 1 3");
//---- buttonsSpacingMediumRadioButton ----
buttonsSpacingMediumRadioButton.setText("Medium");
buttonsSpacingMediumRadioButton.addActionListener(e -> buttonsSpacingChanged());
panel2.add(buttonsSpacingMediumRadioButton, "cell 2 3");
//---- buttonsSpacingLargeRadioButton ----
buttonsSpacingLargeRadioButton.setText("Large");
buttonsSpacingLargeRadioButton.addActionListener(e -> buttonsSpacingChanged());
panel2.add(buttonsSpacingLargeRadioButton, "cell 3 3");
//---- buttonsSpacingHint ----
buttonsSpacingHint.setText("requires Java 17");
buttonsSpacingHint.setForeground(Color.red);
panel2.add(buttonsSpacingHint, "cell 4 3");
//---- fullWindowContentButtonsBoundsLabel ----
fullWindowContentButtonsBoundsLabel.setText("Buttons bounds:");
panel2.add(fullWindowContentButtonsBoundsLabel, "cell 0 4");
//---- fullWindowContentButtonsBoundsField ----
fullWindowContentButtonsBoundsField.setText("null");
panel2.add(fullWindowContentButtonsBoundsField, "cell 1 4 3 1");
//---- nativeButtonsBoundsLabel ----
nativeButtonsBoundsLabel.setText("Native buttons bounds:");
panel2.add(nativeButtonsBoundsLabel, "cell 0 5");
//---- nativeButtonsBoundsField ----
nativeButtonsBoundsField.setText("null");
panel2.add(nativeButtonsBoundsField, "cell 1 5 3 1");
//---- toggleFullScreenButton ----
toggleFullScreenButton.setText("Toggle Full Screen");
toggleFullScreenButton.addActionListener(e -> toggleFullScreen());
panel2.add(toggleFullScreenButton, "cell 0 6");
}
add(panel2, BorderLayout.CENTER);
//---- buttonsSpacingButtonGroup ----
ButtonGroup buttonsSpacingButtonGroup = new ButtonGroup();
buttonsSpacingButtonGroup.add(buttonsSpacingDefaultRadioButton);
buttonsSpacingButtonGroup.add(buttonsSpacingMediumRadioButton);
buttonsSpacingButtonGroup.add(buttonsSpacingLargeRadioButton);
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JPanel placeholderPanel;
private JCheckBox fullWindowContentCheckBox;
private JLabel fullWindowContentHint;
private JCheckBox transparentTitleBarCheckBox;
private JLabel transparentTitleBarHint;
private JCheckBox windowTitleVisibleCheckBox;
private JLabel windowTitleVisibleHint;
private JRadioButton buttonsSpacingDefaultRadioButton;
private JRadioButton buttonsSpacingMediumRadioButton;
private JRadioButton buttonsSpacingLargeRadioButton;
private JLabel buttonsSpacingHint;
private JLabel fullWindowContentButtonsBoundsField;
private JLabel nativeButtonsBoundsField;
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -0,0 +1,191 @@
JFDML JFormDesigner: "8.2.1.0.348" Java: "21.0.1" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
auxiliary() {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "this"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel1"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "placeholderPanel"
"background": sfield java.awt.Color green
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "West"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "First"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[left][left][left][left]para[fill]"
"$rowConstraints": "[][][][fill][][]para[]"
} ) {
name: "panel2"
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "fullWindowContentCheckBox"
"text": "fullWindowContent"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "fullWindowContentChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fullWindowContentHint"
"text": "requires Java 12, 11.0.8 or 8u292"
"foreground": sfield java.awt.Color red
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "transparentTitleBarCheckBox"
"text": "transparentTitleBar"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "transparentTitleBarChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "transparentTitleBarHint"
"text": "requires Java 12, 11.0.8 or 8u292"
"foreground": sfield java.awt.Color red
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "windowTitleVisibleCheckBox"
"text": "windowTitleVisible"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "windowTitleVisibleChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "windowTitleVisibleHint"
"text": "requires Java 17"
"foreground": sfield java.awt.Color red
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "buttonsSpacingLabel"
"text": "Buttons spacing:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JRadioButton" ) {
name: "buttonsSpacingDefaultRadioButton"
"text": "Default"
"$buttonGroup": new FormReference( "buttonsSpacingButtonGroup" )
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "buttonsSpacingChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JRadioButton" ) {
name: "buttonsSpacingMediumRadioButton"
"text": "Medium"
"$buttonGroup": new FormReference( "buttonsSpacingButtonGroup" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "buttonsSpacingChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 3"
} )
add( new FormComponent( "javax.swing.JRadioButton" ) {
name: "buttonsSpacingLargeRadioButton"
"text": "Large"
"$buttonGroup": new FormReference( "buttonsSpacingButtonGroup" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "buttonsSpacingChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "buttonsSpacingHint"
"text": "requires Java 17"
"foreground": sfield java.awt.Color red
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fullWindowContentButtonsBoundsLabel"
"text": "Buttons bounds:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fullWindowContentButtonsBoundsField"
"text": "null"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 4 3 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "nativeButtonsBoundsLabel"
"text": "Native buttons bounds:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "nativeButtonsBoundsField"
"text": "null"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5 3 1"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "toggleFullScreenButton"
"text": "Toggle Full Screen"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "toggleFullScreen", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 725, 350 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "buttonsSpacingButtonGroup"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 360 )
} )
}
}

View File

@@ -32,8 +32,10 @@ public class FlatTestPanel
int width = getWidth();
int height = getHeight();
g.setColor( super.getBackground() );
g.fillRect( 0, 0, width, height );
if( isOpaque() ) {
g.setColor( super.getBackground() );
g.fillRect( 0, 0, width, height );
}
if( isPaintBackgroundPattern() ) {
g.setColor( Color.magenta );

View File

@@ -41,6 +41,9 @@ import net.miginfocom.swing.*;
public class FlatWindowDecorationsTest
extends FlatTestPanel
{
// same as in FlatTitlePane
private static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles";
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
if( SystemInfo.isLinux ) {
@@ -51,7 +54,7 @@ public class FlatWindowDecorationsTest
FlatTestFrame frame = FlatTestFrame.create( args, "FlatWindowDecorationsTest" );
frame.applyComponentOrientationToFrame = true;
UIManager.put( "FlatLaf.debug.titlebar.showRectangles", true );
UIManager.put( KEY_DEBUG_SHOW_RECTANGLES, true );
Class<?> cls = FlatWindowDecorationsTest.class;
List<Image> images = Arrays.asList(
@@ -76,6 +79,14 @@ public class FlatWindowDecorationsTest
initComponents();
}
@Override
public void updateUI() {
super.updateUI();
if( translucentWindowBackgroundCheckBox != null )
translucentWindowBackgroundChanged();
}
@Override
public void addNotify() {
super.addNotify();
@@ -109,6 +120,14 @@ public class FlatWindowDecorationsTest
rootPane.addPropertyChangeListener( "windowDecorationStyle", e -> {
updateDecorationStyleRadioButtons( rootPane );
} );
rootPane.addPropertyChangeListener( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS, e -> {
Rectangle bounds = (Rectangle) e.getNewValue();
if( bounds != null ) {
fullWindowContentButtonsBoundsField.setText( bounds.width + ", " + bounds.height
+ " @ " + bounds.x + ", " + bounds.y );
} else
fullWindowContentButtonsBoundsField.setText( "null" );
} );
}
}
@@ -237,6 +256,27 @@ public class FlatWindowDecorationsTest
}
}
private void translucentWindowBackgroundChanged() {
boolean selected = translucentWindowBackgroundCheckBox.isSelected();
if( selected && !undecoratedCheckBox.isSelected() ) {
undecoratedCheckBox.setSelected( true );
undecoratedChanged();
}
undecoratedCheckBox.setEnabled( !selected );
Color background = selected
? new Color( 100, 0, 0, 100 )
: UIManager.getColor( "control" );
Window window = SwingUtilities.windowForComponent( this );
window.setBackground( background );
for( Component c = this; c != null; c = c.getParent() ) {
if( c instanceof JComponent )
LookAndFeel.installProperty( (JComponent) c, "opaque", !selected );
}
}
private void addMenu() {
JMenu menu = new JMenu( "Hello" );
menu.add( new JMenuItem( "world" ) );
@@ -280,12 +320,21 @@ debug*/
JLabel caption = new JLabel( "Caption" );
caption.setBackground( Color.green );
caption.setOpaque( true );
caption.putClientProperty( FlatClientProperties.COMPONENT_TITLE_BAR_CAPTION, true );
menuBar.add( caption );
menuBar.revalidate();
}
private void addTextField() {
JTextField textField = new JTextField( "text", 10 );
JPanel panel = new JPanel( new GridBagLayout() );
panel.add( textField, new GridBagConstraints() );
menuBar.add( panel );
menuBar.revalidate();
}
private void removeMenu() {
int menuCount = menuBar.getMenuCount();
if( menuCount <= 0 )
@@ -486,13 +535,31 @@ debug*/
rootPane.putClientProperty( FlatClientProperties.TITLE_BAR_SHOW_CLOSE, showCloseCheckBox.isSelected() ? null : false );
}
private void fullWindowContentChanged() {
JRootPane rootPane = getWindowRootPane();
if( rootPane != null ) {
boolean selected = fullWindowContentCheckBox.isSelected();
rootPane.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT, selected ? true : null );
showIconCheckBox.setEnabled( !selected );
showTitleCheckBox.setEnabled( !selected );
}
}
private JRootPane getWindowRootPane() {
Window window = SwingUtilities.windowForComponent( this );
if( window instanceof JFrame )
return ((JFrame)window).getRootPane();
else if( window instanceof JDialog )
return ((JDialog)window).getRootPane();
return null;
return (window instanceof RootPaneContainer)
? ((RootPaneContainer)window).getRootPane()
: null;
}
private void showRectangles() {
JRootPane rootPane = getWindowRootPane();
if( rootPane != null ) {
UIManager.put( KEY_DEBUG_SHOW_RECTANGLES, showRectanglesCheckBox.isSelected() );
rootPane.revalidate();
rootPane.repaint();
}
}
private void initComponents() {
@@ -509,6 +576,9 @@ debug*/
showIconifyCheckBox = new JCheckBox();
showMaximizeCheckBox = new JCheckBox();
showCloseCheckBox = new JCheckBox();
fullWindowContentCheckBox = new JCheckBox();
JLabel fullWindowContentButtonsBoundsLabel = new JLabel();
fullWindowContentButtonsBoundsField = new JLabel();
JPanel panel6 = new JPanel();
menuBarCheckBox = new JCheckBox();
menuBarEmbeddedCheckBox = new JCheckBox();
@@ -519,6 +589,7 @@ debug*/
addMenuButton = new JButton();
addGlueButton = new JButton();
addCaptionButton = new JButton();
addTextFieldButton = new JButton();
removeMenuButton = new JButton();
changeMenuButton = new JButton();
changeTitleButton = new JButton();
@@ -543,11 +614,13 @@ debug*/
colorizeTitleBarCheckBox = new JCheckBox();
colorizeMenuBarCheckBox = new JCheckBox();
colorizeMenusCheckBox = new JCheckBox();
translucentWindowBackgroundCheckBox = new JCheckBox();
JButton openDialogButton = new JButton();
JButton openFrameButton = new JButton();
typeNormalRadioButton = new JRadioButton();
typeUtilityRadioButton = new JRadioButton();
typeSmallRadioButton = new JRadioButton();
showRectanglesCheckBox = new JCheckBox();
menuBar = new JMenuBar();
JMenu fileMenu = new JMenu();
JMenuItem newMenuItem = new JMenuItem();
@@ -586,6 +659,7 @@ debug*/
// rows
"[fill]" +
"[fill]" +
"[]" +
"[]"));
//======== panel7 ========
@@ -643,6 +717,8 @@ debug*/
"[]" +
"[]" +
"[]" +
"[]rel" +
"[]rel" +
"[]"));
//---- showIconCheckBox ----
@@ -673,6 +749,19 @@ debug*/
showCloseCheckBox.setSelected(true);
showCloseCheckBox.addActionListener(e -> showCloseChanged());
panel4.add(showCloseCheckBox, "cell 0 4");
//---- fullWindowContentCheckBox ----
fullWindowContentCheckBox.setText("full window content");
fullWindowContentCheckBox.addActionListener(e -> fullWindowContentChanged());
panel4.add(fullWindowContentCheckBox, "cell 0 5");
//---- fullWindowContentButtonsBoundsLabel ----
fullWindowContentButtonsBoundsLabel.setText("Buttons bounds:");
panel4.add(fullWindowContentButtonsBoundsLabel, "cell 0 6");
//---- fullWindowContentButtonsBoundsField ----
fullWindowContentButtonsBoundsField.setText("null");
panel4.add(fullWindowContentButtonsBoundsField, "cell 0 6");
}
add(panel4, "cell 1 0");
@@ -731,6 +820,7 @@ debug*/
"[]" +
"[]" +
"[]" +
"[]" +
"[]unrel" +
"[]"));
@@ -749,20 +839,25 @@ debug*/
addCaptionButton.addActionListener(e -> addCaption());
panel3.add(addCaptionButton, "cell 0 2");
//---- addTextFieldButton ----
addTextFieldButton.setText("Add text field");
addTextFieldButton.addActionListener(e -> addTextField());
panel3.add(addTextFieldButton, "cell 0 3");
//---- removeMenuButton ----
removeMenuButton.setText("Remove menu");
removeMenuButton.addActionListener(e -> removeMenu());
panel3.add(removeMenuButton, "cell 0 3");
panel3.add(removeMenuButton, "cell 0 4");
//---- changeMenuButton ----
changeMenuButton.setText("Change menu");
changeMenuButton.addActionListener(e -> changeMenu());
panel3.add(changeMenuButton, "cell 0 4");
panel3.add(changeMenuButton, "cell 0 5");
//---- changeTitleButton ----
changeTitleButton.setText("Change title");
changeTitleButton.addActionListener(e -> changeTitle());
panel3.add(changeTitleButton, "cell 0 5");
panel3.add(changeTitleButton, "cell 0 6");
}
add(panel3, "cell 3 0 1 2,aligny top,growy 0");
@@ -885,6 +980,7 @@ debug*/
"[]" +
"[]" +
"[]" +
"[]para" +
"[]"));
//---- unifiedBackgroundCheckBox ----
@@ -906,6 +1002,11 @@ debug*/
colorizeMenusCheckBox.setText("colorize menus");
colorizeMenusCheckBox.addActionListener(e -> colorizeMenus());
panel5.add(colorizeMenusCheckBox, "cell 0 3");
//---- translucentWindowBackgroundCheckBox ----
translucentWindowBackgroundCheckBox.setText("translucent window background");
translucentWindowBackgroundCheckBox.addActionListener(e -> translucentWindowBackgroundChanged());
panel5.add(translucentWindowBackgroundCheckBox, "cell 0 4");
}
add(panel5, "cell 2 1");
@@ -933,6 +1034,12 @@ debug*/
typeSmallRadioButton.setText("Small");
add(typeSmallRadioButton, "cell 0 2 3 1");
//---- showRectanglesCheckBox ----
showRectanglesCheckBox.setText("Show debug title bar rectangles");
showRectanglesCheckBox.setSelected(true);
showRectanglesCheckBox.addActionListener(e -> showRectangles());
add(showRectanglesCheckBox, "cell 0 3");
//======== menuBar ========
{
@@ -1140,6 +1247,8 @@ debug*/
private JCheckBox showIconifyCheckBox;
private JCheckBox showMaximizeCheckBox;
private JCheckBox showCloseCheckBox;
private JCheckBox fullWindowContentCheckBox;
private JLabel fullWindowContentButtonsBoundsField;
private JCheckBox menuBarCheckBox;
private JCheckBox menuBarEmbeddedCheckBox;
private JCheckBox menuBarVisibleCheckBox;
@@ -1148,6 +1257,7 @@ debug*/
private JButton addMenuButton;
private JButton addGlueButton;
private JButton addCaptionButton;
private JButton addTextFieldButton;
private JButton removeMenuButton;
private JButton changeMenuButton;
private JButton changeTitleButton;
@@ -1169,9 +1279,11 @@ debug*/
private JCheckBox colorizeTitleBarCheckBox;
private JCheckBox colorizeMenuBarCheckBox;
private JCheckBox colorizeMenusCheckBox;
private JCheckBox translucentWindowBackgroundCheckBox;
private JRadioButton typeNormalRadioButton;
private JRadioButton typeUtilityRadioButton;
private JRadioButton typeSmallRadioButton;
private JCheckBox showRectanglesCheckBox;
private JMenuBar menuBar;
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.1.0.0.283" Java: "19.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.1.0.348" Java: "21.0.1" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -9,7 +9,7 @@ new FormModel {
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[left][fill][fill][fill]"
"$rowConstraints": "[fill][fill][]"
"$rowConstraints": "[fill][fill][][]"
} ) {
name: "this"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
@@ -77,7 +77,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,hidemode 3,gap 0 0"
"$columnConstraints": "[grow,left]"
"$rowConstraints": "[][][][][]"
"$rowConstraints": "[][][][][]rel[]rel[]"
} ) {
name: "panel4"
"border": new javax.swing.border.TitledBorder( "Title Bar" )
@@ -135,6 +135,31 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "fullWindowContentCheckBox"
"text": "full window content"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "fullWindowContentChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fullWindowContentButtonsBoundsLabel"
"text": "Buttons bounds:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fullWindowContentButtonsBoundsField"
"text": "null"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
@@ -204,7 +229,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill]"
"$rowConstraints": "[][][][][]unrel[]"
"$rowConstraints": "[][][][][][]unrel[]"
} ) {
name: "panel3"
add( new FormComponent( "javax.swing.JButton" ) {
@@ -237,6 +262,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "addTextFieldButton"
"text": "Add text field"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "addTextField", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "removeMenuButton"
"text": "Remove menu"
@@ -245,7 +280,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "removeMenu", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
"value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "changeMenuButton"
@@ -255,7 +290,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeMenu", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
"value": "cell 0 5"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "changeTitleButton"
@@ -265,7 +300,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeTitle", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
"value": "cell 0 6"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 0 1 2,aligny top,growy 0"
@@ -449,7 +484,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,hidemode 3,gap 0 0"
"$columnConstraints": "[left]"
"$rowConstraints": "[][][][]"
"$rowConstraints": "[][][][]para[]"
} ) {
name: "panel5"
"border": new javax.swing.border.TitledBorder( "Color" )
@@ -493,6 +528,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "translucentWindowBackgroundCheckBox"
"text": "translucent window background"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "translucentWindowBackgroundChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1"
} )
@@ -542,6 +587,17 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2 3 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showRectanglesCheckBox"
"text": "Show debug title bar rectangles"
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showRectangles", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 960, 495 )

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.testing.extras;
import java.awt.*;
import javax.swing.*;
import com.formdev.flatlaf.extras.*;
import com.formdev.flatlaf.extras.components.*;
@@ -75,6 +76,12 @@ public class FlatExtrasTest
disabledTabbedPane2.setIconAt( 0, icon );
disabledTabbedPane2.setDisabledIconAt( 0, disabledIcon );
addJSVGIcon( "linearGradient.svg", 64, 128 );
addJSVGIcon( "stripes.svg", 128, 128 );
addJSVGIcon( "gradientText0.svg", 128, 128 );
addJSVGIcon( "gradientText1.svg", 128, 128 );
addJSVGIcon( "gradientText2.svg", 128, 128 );
disabledChanged();
}
@@ -82,6 +89,10 @@ public class FlatExtrasTest
svgIconsPanel.add( new JLabel( new FlatSVGIcon( "com/formdev/flatlaf/demo/extras/svg/" + name ) ) );
}
private void addJSVGIcon( String name, int width, int height ) {
gradientIconsPanel.add( new JLabel( new FlatSVGIcon( "com/formdev/flatlaf/testing/extras/jsvg/" + name, width, height ) ) );
}
private void triStateCheckBox1Changed() {
triStateLabel1.setText( triStateCheckBox1.getState().toString() );
}
@@ -104,6 +115,9 @@ public class FlatExtrasTest
disabledLabel2.setEnabled( enabled );
disabledButton2.setEnabled( enabled );
disabledTabbedPane2.setEnabledAt( 0, enabled );
for( Component c : gradientIconsPanel.getComponents() )
c.setEnabled( enabled );
}
@Override
@@ -141,6 +155,7 @@ public class FlatExtrasTest
disabledButton2 = new JButton();
disabledTabbedPane2 = new JTabbedPane();
label6 = new JLabel();
gradientIconsPanel = new JPanel();
//======== this ========
setLayout(new MigLayout(
@@ -156,6 +171,7 @@ public class FlatExtrasTest
"[]" +
"[]" +
"[]" +
"[]" +
"[]"));
//---- label1 ----
@@ -251,6 +267,12 @@ public class FlatExtrasTest
label6.setText("setIcon() and setDisabledIcon()");
label6.setEnabled(false);
add(label6, "cell 1 6 2 1,gapx 20");
//======== gradientIconsPanel ========
{
gradientIconsPanel.setLayout(new FlowLayout());
}
add(gradientIconsPanel, "cell 1 7 2 1");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
@@ -275,5 +297,6 @@ public class FlatExtrasTest
private JButton disabledButton2;
private JTabbedPane disabledTabbedPane2;
private JLabel label6;
private JPanel gradientIconsPanel;
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.3.1.342" Java: "15" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.0.0.331" Java: "21" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -6,7 +6,7 @@ new FormModel {
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[][][left]"
"$rowConstraints": "[][][][][][][]"
"$rowConstraints": "[][][][][][][][]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -143,9 +143,14 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6 2 1,gapx 20"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "gradientIconsPanel"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 7 2 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 595, 470 )
"size": new java.awt.Dimension( 645, 470 )
} )
}
}

View File

@@ -17,6 +17,7 @@
package com.formdev.flatlaf.testing.jideoss;
import java.awt.*;
import java.util.Random;
import javax.swing.*;
import javax.swing.border.*;
import com.formdev.flatlaf.FlatClientProperties;
@@ -140,6 +141,20 @@ public class FlatJideOssContainerTest
tabbedPane.addTab( "Tab 4", new JLabel( "non-opaque content", SwingConstants.CENTER ) );
break;
case 4:
tabbedPane.addTab( "Tab 5", new JLabel( "random background content", SwingConstants.CENTER ) {
Random random = new Random();
@Override
protected void paintComponent( Graphics g ) {
g.setColor( new Color( random.nextInt() ) );
g.fillRect( 0, 0, getWidth(), getHeight() );
super.paintComponent( g );
}
} );
break;
default:
int index = tabbedPane.getTabCount() + 1;
tabbedPane.addTab( "Tab " + index, createTab( "tab content " + index ) );

View File

@@ -119,10 +119,9 @@ public class UIDefaultsDump
//
// dump( "com.jgoodies.looks.plastic.PlasticLookAndFeel", dir, false );
// dump( "com.jgoodies.looks.windows.WindowsLookAndFeel", dir, false );
// dump( "com.alee.laf.WebLookAndFeel", dir, false );
// try {
// EventQueue.invokeAndWait( () -> {
// dump( "org.pushingpixels.substance.api.skin.SubstanceGraphiteAquaLookAndFeel", dir, false );
// SwingUtilities.invokeAndWait( () -> {
// dump( "org.pushingpixels.radiance.theming.api.skin.RadianceGraphiteAquaLookAndFeel", dir, false );
// } );
// } catch( Exception ex ) {
// // TODO Auto-generated catch block
@@ -673,7 +672,7 @@ public class UIDefaultsDump
switch( borderClassName ) {
case "com.apple.laf.AquaToolBarUI$ToolBarBorder":
case "org.pushingpixels.substance.internal.utils.border.SubstanceToolBarBorder":
case "org.pushingpixels.radiance.theming.internal.utils.border.RadianceToolBarBorder":
c = new JToolBar();
break;

View File

@@ -68,6 +68,20 @@ public class UIDefaultsKeysDump
collectKeys( FlatDarculaLaf.class.getName(), keys );
collectKeys( FlatTestLaf.class.getName(), keys );
// remove unused keys (defined in BasicLookAndFeel)
keys.remove( "Button.textIconGap" );
keys.remove( "Button.textShiftOffset" );
keys.remove( "CheckBox.textIconGap" );
keys.remove( "CheckBox.textShiftOffset" );
keys.remove( "RadioButton.textIconGap" );
keys.remove( "RadioButton.textShiftOffset" );
keys.remove( "TabbedPane.contentOpaque" );
keys.remove( "TabbedPane.selectedTabPadInsets" );
keys.remove( "TabbedPane.shadow" );
keys.remove( "TabbedPane.tabsOverlapBorder" );
keys.remove( "ToggleButton.textIconGap" );
keys.remove( "ToggleButton.textShiftOffset" );
// write key file
try( Writer fileWriter = new BufferedWriter( new OutputStreamWriter(
new FileOutputStream( keysFile ), StandardCharsets.UTF_8 ) ) )

View File

@@ -0,0 +1,3 @@
The SVG icons in this folder are from JSVG,
which is licensed under the MIT License
See: https://github.com/weisJ/jsvg

View File

@@ -0,0 +1,12 @@
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"
stroke="none">
<defs>
<linearGradient id="myGradient"
gradientTransform="rotate(90)">
<stop offset="5%" stop-color="gold" />
<stop offset="95%" stop-color="red" />
</linearGradient>
</defs>
<rect x="0" y="50" width="100" height="40" fill="url(#myGradient)" />
<text x="0" y="50" fill="url(#myGradient)">Hello lovely World!</text>
</svg>

After

Width:  |  Height:  |  Size: 417 B

View File

@@ -0,0 +1,14 @@
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"
stroke="none">
<defs>
<linearGradient id="myGradient">
<stop offset="5%" stop-color="gold" />
<stop offset="95%" stop-color="red" />
</linearGradient>
</defs>
<text x="0" y="50">
Hello
<tspan fill="url(#myGradient)">lovely</tspan>
World!
</text>
</svg>

After

Width:  |  Height:  |  Size: 336 B

View File

@@ -0,0 +1,14 @@
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"
stroke="none">
<defs>
<linearGradient id="myGradient">
<stop offset="5%" stop-color="gold" />
<stop offset="95%" stop-color="red" />
</linearGradient>
</defs>
<text x="0" y="50" fill="url(#myGradient)">
Hello
<tspan>lovely</tspan>
World!
</text>
</svg>

After

Width:  |  Height:  |  Size: 336 B

View File

@@ -0,0 +1,23 @@
<svg width="120" height="240" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="Gradient1"
gradientUnits="objectBoundingBox">
<stop offset="0%" stop-color="red" />
<stop offset="50%" stop-color="black" stop-opacity="0" />
<stop offset="100%" stop-color="blue" />
</linearGradient>
<linearGradient id="Gradient2" x1="0" x2="0" y1="0"
y2="1">
<stop offset="0%" stop-color="red" />
<stop offset="50%" stop-color="black" stop-opacity="0" />
<stop offset="100%" stop-color="blue" />
</linearGradient>
</defs>
<rect id="rect1" x="10" y="10" rx="15" ry="15" width="100"
height="100" fill="url(#Gradient1)" />
<rect x="10" y="120" rx="15" ry="15" width="100" height="100"
fill="url(#Gradient2)" />
</svg>

After

Width:  |  Height:  |  Size: 767 B

View File

@@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" width="500px" height="500px">
<defs>
<linearGradient id="stripes">
<stop offset="0%" stop-color="red" />
<stop offset="10%" stop-color="red" />
<stop offset="10%" stop-color="orange" />
<stop offset="20%" stop-color="orange" />
<stop offset="20%" stop-color="yellow" />
<stop offset="30%" stop-color="yellow" />
<stop offset="30%" stop-color="lightgreen" />
<stop offset="40%" stop-color="lightgreen" />
<stop offset="40%" stop-color="green" />
<stop offset="50%" stop-color="green" />
<stop offset="50%" stop-color="purple" />
<stop offset="60%" stop-color="purple" />
<stop offset="60%" stop-color="blue" />
<stop offset="70%" stop-color="blue" />
<stop offset="70%" stop-color="skyblue" />
<stop offset="80%" stop-color="skyblue" />
<stop offset="80%" stop-color="pink" />
<stop offset="90%" stop-color="pink" />
<stop offset="90%" stop-color="white" />
<stop offset="100%" stop-color="white" />
</linearGradient>
</defs>
<rect width="100%" height="100%" fill="url(#stripes)" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -64,8 +64,6 @@ Button.selectedForeground
Button.shadow
Button.startBackground
Button.startBorderColor
Button.textIconGap
Button.textShiftOffset
Button.toolbar.disabledSelectedBackground
Button.toolbar.disabledSelectedForeground
Button.toolbar.hoverBackground
@@ -149,8 +147,6 @@ CheckBox.icon[filled].selectedBorderColor
CheckBox.icon[filled].selectedBorderWidth
CheckBox.margin
CheckBox.rollover
CheckBox.textIconGap
CheckBox.textShiftOffset
CheckBoxMenuItem.acceleratorForeground
CheckBoxMenuItem.acceleratorSelectionForeground
CheckBoxMenuItem.arrowIcon
@@ -655,8 +651,6 @@ RadioButton.light
RadioButton.margin
RadioButton.rollover
RadioButton.shadow
RadioButton.textIconGap
RadioButton.textShiftOffset
RadioButtonMenuItem.acceleratorForeground
RadioButtonMenuItem.acceleratorSelectionForeground
RadioButtonMenuItem.arrowIcon
@@ -855,7 +849,6 @@ TabbedPane.closePressedBackground
TabbedPane.closePressedForeground
TabbedPane.closeSize
TabbedPane.contentAreaColor
TabbedPane.contentOpaque
TabbedPane.contentSeparatorHeight
TabbedPane.darkShadow
TabbedPane.disabledForeground
@@ -881,9 +874,7 @@ TabbedPane.selectedBackground
TabbedPane.selectedForeground
TabbedPane.selectedInsets
TabbedPane.selectedLabelShift
TabbedPane.selectedTabPadInsets
TabbedPane.selectionFollowsFocus
TabbedPane.shadow
TabbedPane.showTabSeparators
TabbedPane.tabAlignment
TabbedPane.tabArc
@@ -901,7 +892,6 @@ TabbedPane.tabSeparatorsFullHeight
TabbedPane.tabType
TabbedPane.tabWidthMode
TabbedPane.tabsOpaque
TabbedPane.tabsOverlapBorder
TabbedPane.tabsPopupPolicy
TabbedPane.textIconGap
TabbedPane.underlineColor
@@ -920,6 +910,7 @@ Table.dropCellBackground
Table.dropCellForeground
Table.dropLineColor
Table.dropLineShortColor
Table.editorSelectAllOnStartEditing
Table.focusCellBackground
Table.focusCellForeground
Table.focusCellHighlightBorder
@@ -928,6 +919,7 @@ Table.font
Table.foreground
Table.gridColor
Table.intercellSpacing
Table.paintOutsideAlternateRows
Table.rowHeight
Table.scrollPaneBorder
Table.selectionBackground
@@ -1047,7 +1039,6 @@ TitlePane.inactiveBackground
TitlePane.inactiveForeground
TitlePane.maximizeIcon
TitlePane.menuBarEmbedded
TitlePane.menuBarResizeHeight
TitlePane.menuBarTitleGap
TitlePane.menuBarTitleMinimumGap
TitlePane.noIconLeftGap
@@ -1079,7 +1070,6 @@ TitlePane.small.iconifyIcon
TitlePane.small.inactiveBackground
TitlePane.small.inactiveForeground
TitlePane.small.maximizeIcon
TitlePane.small.menuBarResizeHeight
TitlePane.small.menuBarTitleGap
TitlePane.small.menuBarTitleMinimumGap
TitlePane.small.noIconLeftGap
@@ -1130,8 +1120,6 @@ ToggleButton.tab.selectedBackground
ToggleButton.tab.selectedForeground
ToggleButton.tab.underlineColor
ToggleButton.tab.underlineHeight
ToggleButton.textIconGap
ToggleButton.textShiftOffset
ToggleButton.toolbar.disabledSelectedBackground
ToggleButton.toolbar.disabledSelectedForeground
ToggleButton.toolbar.hoverBackground

View File

@@ -14,8 +14,8 @@
# limitations under the License.
#
flatlaf.releaseVersion = 3.3
flatlaf.developmentVersion = 3.4-SNAPSHOT
flatlaf.releaseVersion = 3.4.1
flatlaf.developmentVersion = 3.5-SNAPSHOT
org.gradle.parallel = true
# org.gradle.warning.mode = all

View File

@@ -15,7 +15,7 @@
#
[versions]
junit = "5.10.0"
junit = "5.10.2"
[libraries]
@@ -24,10 +24,10 @@ junit = "5.10.0"
sigtest = "org.netbeans.tools:sigtest-maven-plugin:1.7"
# flatlaf-extras
jsvg = "com.github.weisj:jsvg:1.2.0"
jsvg = "com.github.weisj:jsvg:1.4.0"
# flatlaf-jide-oss
jide-oss = "com.formdev:jide-oss:3.7.14"
jide-oss = "com.formdev:jide-oss:3.7.15"
# flatlaf-swingx
swingx-all = "org.swinglabs.swingx:swingx-all:1.6.5-1"
@@ -50,16 +50,12 @@ jna = "net.java.dev.jna:jna:5.12.1"
jna-platform = "net.java.dev.jna:jna-platform:5.12.1"
# junit
junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
junit = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
junit-launcher = { module = "org.junit.platform:junit-platform-launcher" }
# errorprone
errorprone = "com.google.errorprone:error_prone_core:2.20.0"
[bundles]
junit = [ "junit-api", "junit-params" ]
[plugins]
errorprone = { id = "net.ltgt.errorprone", version = "3.1.0" }

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