Compare commits

...

35 Commits

Author SHA1 Message Date
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
67 changed files with 3682 additions and 843 deletions

View File

@@ -9,6 +9,14 @@ on:
- '*' - '*'
tags: tags:
- '[0-9]*' - '[0-9]*'
paths-ignore:
- '**.md'
- '.*'
- '**/.settings/**'
- 'flatlaf-core/svg/**'
- 'flatlaf-testing/dumps/**'
- 'flatlaf-testing/misc/**'
- 'images/**'
jobs: jobs:
build: build:
@@ -32,7 +40,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1 - uses: gradle/wrapper-validation-action@v2
if: matrix.java == '8' if: matrix.java == '8'
- name: Setup Java ${{ matrix.java }} - name: Setup Java ${{ matrix.java }}

View File

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

View File

@@ -13,6 +13,8 @@ on:
- 'flatlaf-natives/**' - 'flatlaf-natives/**'
- '.github/workflows/natives.yml' - '.github/workflows/natives.yml'
- 'gradle/wrapper/gradle-wrapper.properties' - 'gradle/wrapper/gradle-wrapper.properties'
- '!**.md'
- '!**/.settings/**'
jobs: jobs:
Natives: Natives:
@@ -28,7 +30,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1 - uses: gradle/wrapper-validation-action@v2
- name: Setup Java 11 - name: Setup Java 11
uses: actions/setup-java@v4 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,35 @@
FlatLaf Change Log FlatLaf Change Log
================== ==================
## 3.4-SNAPSHOT ## 3.4
#### New features and improvements #### New features and improvements
- Native libraries: System property `flatlaf.nativeLibraryPath` now supports - FlatLaf window decorations (Windows 10/11 and Linux): Support "full window
loading native libraries named the same as on Maven central. Improved log content" mode, which allows you to extend the content into the window title
messages for loading fails. 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 - Fonts: Updated **Inter** to
[v4.0](https://github.com/rsms/inter/releases/tag/v4.0). [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 #### 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 - JIDE CommandMenuBar: Fixed `ClassCastException` when JIDE command bar displays
`JideMenu` in popup. (PR #794) `JideMenu` in popup. (PR #794)

View File

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

View File

@@ -124,7 +124,7 @@ tasks.withType<Sign>().configureEach {
} }
// check whether parallel build is enabled // check whether parallel build is enabled
tasks.withType<PublishToMavenRepository>().configureEach { tasks.withType<AbstractPublishToMaven>().configureEach {
doFirst { doFirst {
if( System.getProperty( "org.gradle.parallel" ) == "true" ) if( System.getProperty( "org.gradle.parallel" ) == "true" )
throw RuntimeException( "Publishing does not work correctly with enabled parallel build. Disable parallel build with VM option '-Dorg.gradle.parallel=false'." ) throw RuntimeException( "Publishing does not work correctly with enabled parallel build. Disable parallel build with VM option '-Dorg.gradle.parallel=false'." )

View File

@@ -61,6 +61,27 @@ tasks {
archiveBaseName.set( "flatlaf" ) archiveBaseName.set( "flatlaf" )
} }
register<Zip>( "jarNoNatives" ) {
group = "build"
dependsOn( "jar" )
archiveBaseName.set( "flatlaf" )
archiveClassifier.set( "no-natives" )
archiveExtension.set( "jar" )
destinationDirectory = layout.buildDirectory.dir( "libs" )
from( zipTree( jar.get().archiveFile.get().asFile ) )
exclude( "com/formdev/flatlaf/natives/**" )
}
withType<AbstractPublishToMaven>().configureEach {
dependsOn( "jarNoNatives" )
}
withType<Sign>().configureEach {
dependsOn( "jarNoNatives" )
}
check { check {
dependsOn( "sigtestCheck" ) dependsOn( "sigtestCheck" )
} }
@@ -127,6 +148,8 @@ flatlafPublish {
val natives = "src/main/resources/com/formdev/flatlaf/natives" val natives = "src/main/resources/com/formdev/flatlaf/natives"
nativeArtifacts = listOf( nativeArtifacts = listOf(
NativeArtifact( tasks.getByName( "jarNoNatives" ).outputs.files.asPath, "no-natives", "jar" ),
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ), NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ), NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ), NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,6 +22,7 @@ import java.security.CodeSource;
import com.formdev.flatlaf.FlatSystemProperties; import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.NativeLibrary; import com.formdev.flatlaf.util.NativeLibrary;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
/** /**
@@ -177,21 +178,39 @@ class FlatNativeLibrary
// build library file // build library file
String libraryName = buildLibraryName( jarFile, classifier, ext ); String libraryName = buildLibraryName( jarFile, classifier, ext );
File parent = jarFile.getParentFile(); File jarDir = jarFile.getParentFile();
// check whether native library exists in same directory as jar // 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() ) if( libraryFile.isFile() )
return libraryFile; return libraryFile;
// if jar is in "lib" directory, then also check whether native library exists // if jar is in "lib" directory, then also check whether native library exists
// in "../bin" directory // in "../bin" directory
if( parent.getName().equalsIgnoreCase( "lib" ) ) { if( jarDir.getName().equalsIgnoreCase( "lib" ) ) {
libraryFile = new File( parent.getParentFile(), "bin/" + libraryName ); libraryFile = new File( jarDir.getParentFile(), "bin/" + libraryName );
if( libraryFile.isFile() ) if( libraryFile.isFile() )
return libraryFile; 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 // native library not found
return null; return null;
} }
@@ -224,6 +243,10 @@ class FlatNativeLibrary
private static String buildLibraryName( File jarFile, String classifier, String ext ) { private static String buildLibraryName( File jarFile, String classifier, String ext ) {
String jarName = jarFile.getName(); String jarName = jarFile.getName();
String jarBasename = jarName.substring( 0, jarName.lastIndexOf( '.' ) ); String jarBasename = jarName.substring( 0, jarName.lastIndexOf( '.' ) );
// remove classifier "no-natives" (if used)
jarBasename = StringUtils.removeTrailing( jarBasename, "-no-natives" );
return jarBasename return jarBasename
+ (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf") + (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf")
+ '-' + classifier + '.' + ext; + '-' + classifier + '.' + ext;

View File

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

View File

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

View File

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

View File

@@ -84,7 +84,7 @@ import com.formdev.flatlaf.util.UIScale;
*/ */
public class FlatSplitPaneUI public class FlatSplitPaneUI
extends BasicSplitPaneUI extends BasicSplitPaneUI
implements StyleableUI implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
{ {
@Styleable protected String arrowType; @Styleable protected String arrowType;
/** @since 3.3 */ @Styleable protected Color draggingColor; /** @since 3.3 */ @Styleable protected Color draggingColor;
@@ -227,6 +227,15 @@ public class FlatSplitPaneUI
((FlatSplitPaneDivider)divider).paintStyle( g, x, y, width, height ); ((FlatSplitPaneDivider)divider).paintStyle( g, x, y, width, height );
} }
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
/** @since 3.4 */
@Override
public Boolean isTitleBarCaptionAt( int x, int y ) {
// necessary because BasicSplitPaneDivider adds some mouse listeners for dragging divider
return null; // check children
}
//---- class FlatSplitPaneDivider ----------------------------------------- //---- class FlatSplitPaneDivider -----------------------------------------
protected class FlatSplitPaneDivider protected class FlatSplitPaneDivider

View File

@@ -182,7 +182,7 @@ import com.formdev.flatlaf.util.UIScale;
*/ */
public class FlatTabbedPaneUI public class FlatTabbedPaneUI
extends BasicTabbedPaneUI extends BasicTabbedPaneUI
implements StyleableUI implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
{ {
// tab type // tab type
/** @since 2 */ protected static final int TAB_TYPE_UNDERLINED = 0; /** @since 2 */ protected static final int TAB_TYPE_UNDERLINED = 0;
@@ -2300,6 +2300,17 @@ debug*/
return (rects[last].y + rects[last].height) - rects[0].y; return (rects[last].y + rects[last].height) - rects[0].y;
} }
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
/** @since 3.4 */
@Override
public Boolean isTitleBarCaptionAt( int x, int y ) {
if( tabForCoordinate( tabPane, x, y ) >= 0 )
return false;
return null; // check children
}
//---- class TabCloseButton ----------------------------------------------- //---- class TabCloseButton -----------------------------------------------
private static class TabCloseButton private static class TabCloseButton
@@ -3985,10 +3996,8 @@ debug*/
//---- class RunWithOriginalLayoutManagerDelegateAction ------------------- //---- class RunWithOriginalLayoutManagerDelegateAction -------------------
private static class RunWithOriginalLayoutManagerDelegateAction private static class RunWithOriginalLayoutManagerDelegateAction
implements Action extends FlatUIAction
{ {
private final Action delegate;
static void install( ActionMap map, String key ) { static void install( ActionMap map, String key ) {
Action oldAction = map.get( key ); Action oldAction = map.get( key );
if( oldAction == null || oldAction instanceof RunWithOriginalLayoutManagerDelegateAction ) if( oldAction == null || oldAction instanceof RunWithOriginalLayoutManagerDelegateAction )
@@ -3998,24 +4007,9 @@ debug*/
} }
private RunWithOriginalLayoutManagerDelegateAction( Action delegate ) { private RunWithOriginalLayoutManagerDelegateAction( Action delegate ) {
this.delegate = delegate; super( delegate );
} }
@Override
public Object getValue( String key ) {
return delegate.getValue( key );
}
@Override
public boolean isEnabled() {
return delegate.isEnabled();
}
@Override public void putValue( String key, Object value ) {}
@Override public void setEnabled( boolean b ) {}
@Override public void addPropertyChangeListener( PropertyChangeListener listener ) {}
@Override public void removePropertyChangeListener( PropertyChangeListener listener ) {}
@Override @Override
public void actionPerformed( ActionEvent e ) { public void actionPerformed( ActionEvent e ) {
JTabbedPane tabbedPane = (JTabbedPane) e.getSource(); JTabbedPane tabbedPane = (JTabbedPane) e.getSource();

View File

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

View File

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

View File

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

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

View File

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

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

View File

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

View File

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

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

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

@@ -56,7 +56,7 @@ tasks {
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
} }
withType<PublishToMavenRepository>().configureEach { withType<AbstractPublishToMaven>().configureEach {
onlyIf { !rootProject.hasProperty( "skipFonts" ) } onlyIf { !rootProject.hasProperty( "skipFonts" ) }
} }
} }

View File

@@ -56,7 +56,7 @@ tasks {
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
} }
withType<PublishToMavenRepository>().configureEach { withType<AbstractPublishToMaven>().configureEach {
onlyIf { !rootProject.hasProperty( "skipFonts" ) } onlyIf { !rootProject.hasProperty( "skipFonts" ) }
} }
} }

View File

@@ -56,7 +56,7 @@ tasks {
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
} }
withType<PublishToMavenRepository>().configureEach { withType<AbstractPublishToMaven>().configureEach {
onlyIf { !rootProject.hasProperty( "skipFonts" ) } onlyIf { !rootProject.hasProperty( "skipFonts" ) }
} }
} }

View File

@@ -56,7 +56,7 @@ tasks {
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
} }
withType<PublishToMavenRepository>().configureEach { withType<AbstractPublishToMaven>().configureEach {
onlyIf { !rootProject.hasProperty( "skipFonts" ) } onlyIf { !rootProject.hasProperty( "skipFonts" ) }
} }
} }

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.jideoss.ui; 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_HAS_FULL_BORDER;
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_SHOW_TAB_SEPARATORS; import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_SHOW_TAB_SEPARATORS;
import static com.formdev.flatlaf.FlatClientProperties.clientPropertyBoolean; import static com.formdev.flatlaf.FlatClientProperties.clientPropertyBoolean;
@@ -30,6 +31,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.awt.LayoutManager; import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Shape; import java.awt.Shape;
import java.awt.event.MouseListener; import java.awt.event.MouseListener;
@@ -37,6 +39,7 @@ import java.awt.event.MouseMotionListener;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.function.Function;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -100,6 +103,25 @@ public class FlatJideTabbedPaneUI
return new 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 @Override
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();

View File

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

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, const char* className, const char* fieldName, const char* fieldSignature, bool staticField );
jmethodID getMethodID( JNIEnv *env, jclass cls, const char* methodName, const char* methodSignature, bool staticMethod );

View File

@@ -7,6 +7,12 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #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 * Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
* Method: setWindowRoundedBorder * Method: setWindowRoundedBorder
@@ -15,6 +21,38 @@ extern "C" {
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder
(JNIEnv *, jclass, jobject, jfloat, jfloat, jint); (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 #ifdef __cplusplus
} }
#endif #endif

View File

@@ -21,8 +21,8 @@
* @author Karl Tauber * @author Karl Tauber
*/ */
jfieldID getFieldID( JNIEnv *env, const char* className, const char* fieldName, const char* fieldSignature ) { jclass findClass( JNIEnv *env, const char* className, bool globalRef ) {
// NSLog( @"getFieldID %s %s %s", className, fieldName, fieldSignature ); // NSLog( @"findClass %s", className );
jclass cls = env->FindClass( className ); jclass cls = env->FindClass( className );
if( cls == NULL ) { if( cls == NULL ) {
@@ -32,7 +32,22 @@ jfieldID getFieldID( JNIEnv *env, const char* className, const char* fieldName,
return NULL; return NULL;
} }
jfieldID fieldID = env->GetFieldID( cls, fieldName, fieldSignature ); if( globalRef )
cls = reinterpret_cast<jclass>( env->NewGlobalRef( cls ) );
return cls;
}
jfieldID getFieldID( JNIEnv *env, const char* className, const char* fieldName, const char* fieldSignature, bool staticField ) {
// NSLog( @"getFieldID %s %s %s", className, fieldName, fieldSignature );
jclass cls = findClass( env, className, false );
if( cls == NULL )
return NULL;
jfieldID fieldID = staticField
? env->GetStaticFieldID( cls, fieldName, fieldSignature )
: env->GetFieldID( cls, fieldName, fieldSignature );
if( fieldID == NULL ) { 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' in class '%s'", fieldName, fieldSignature, className );
env->ExceptionDescribe(); // print stack trace env->ExceptionDescribe(); // print stack trace
@@ -42,3 +57,22 @@ jfieldID getFieldID( JNIEnv *env, const char* className, const char* fieldName,
return fieldID; 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 <Cocoa/Cocoa.h>
#import <objc/runtime.h>
#import <jni.h> #import <jni.h>
#import "JNIUtils.h" #import "JNIUtils.h"
#import "JNFRunLoop.h" #import "JNFRunLoop.h"
@@ -24,14 +25,37 @@
* @author Karl Tauber * @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 ) { NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ) {
if( window == NULL ) if( window == NULL )
return NULL; return NULL;
// initialize field IDs (done only once because fields are static) // initialize field IDs (done only once because variables are static)
static jfieldID peerID = getFieldID( env, "java/awt/Component", "peer", "Ljava/awt/peer/ComponentPeer;" ); static jfieldID peerID = getFieldID( env, "java/awt/Component", "peer", "Ljava/awt/peer/ComponentPeer;", false );
static jfieldID platformWindowID = getFieldID( env, "sun/lwawt/LWWindowPeer", "platformWindow", "Lsun/lwawt/PlatformWindow;" ); static jfieldID platformWindowID = getFieldID( env, "sun/lwawt/LWWindowPeer", "platformWindow", "Lsun/lwawt/PlatformWindow;", false );
static jfieldID ptrID = getFieldID( env, "sun/lwawt/macosx/CFRetainedResource", "ptr", "J" ); static jfieldID ptrID = getFieldID( env, "sun/lwawt/macosx/CFRetainedResource", "ptr", "J", false );
if( peerID == NULL || platformWindowID == NULL || ptrID == NULL ) if( peerID == NULL || platformWindowID == NULL || ptrID == NULL )
return NULL; return NULL;
@@ -49,6 +73,16 @@ NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ) {
return (NSWindow *) jlong_to_ptr( env->GetLongField( platformWindow, ptrID ) ); 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" extern "C"
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder
( JNIEnv* env, jclass cls, jobject window, jfloat radius, jfloat borderWidth, jint borderColor ) ( JNIEnv* env, jclass cls, jobject window, jfloat radius, jfloat borderWidth, jint borderColor )
@@ -87,3 +121,259 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setW
JNI_COCOA_EXIT() JNI_COCOA_EXIT()
return FALSE; 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

@@ -41,6 +41,8 @@ dependencies {
implementation( libs.jide.oss ) implementation( libs.jide.oss )
implementation( libs.glazedlists ) implementation( libs.glazedlists )
implementation( libs.netbeans.api.awt ) implementation( libs.netbeans.api.awt )
components.all<TargetJvmVersion8Rule>()
} }
applyLafs() applyLafs()
@@ -58,3 +60,13 @@ fun applyLafs() {
dependencies.implementation( parts[2] ) 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.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.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.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.focusCellBackground #46494b HSL 204 3 28 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellForeground #bbbbbb HSL 0 0 73 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 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.foreground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI]
Table.gridColor #5a5e60 HSL 200 3 36 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.intercellSpacing 0,0 javax.swing.plaf.DimensionUIResource [UI]
Table.paintOutsideAlternateRows false
Table.rowHeight 20 Table.rowHeight 20
Table.scrollPaneBorder [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatScrollPaneBorder [UI] 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] 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.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40 TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12 TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 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.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.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.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.focusCellBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellForeground #000000 HSL 0 0 0 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 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.foreground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI]
Table.gridColor #ebebeb HSL 0 0 92 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.intercellSpacing 0,0 javax.swing.plaf.DimensionUIResource [UI]
Table.paintOutsideAlternateRows false
Table.rowHeight 20 Table.rowHeight 20
Table.scrollPaneBorder [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatScrollPaneBorder [UI] 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] 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.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40 TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12 TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 TitlePane.noIconLeftGap 8

View File

@@ -322,7 +322,7 @@ OS Windows 10
#---- javax.swing.JMenuBar ---- #---- javax.swing.JMenuBar ----
1 javax.swing.plaf.basic.LazyActionMap [UI] 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 ---- #---- javax.swing.JMenuItem ----
@@ -613,7 +613,7 @@ OS Windows 10
selectPreviousRowCell javax.swing.plaf.basic.BasicTableUI$Actions selectPreviousRowCell javax.swing.plaf.basic.BasicTableUI$Actions
selectPreviousRowChangeLead javax.swing.plaf.basic.BasicTableUI$Actions selectPreviousRowChangeLead javax.swing.plaf.basic.BasicTableUI$Actions
selectPreviousRowExtendSelection 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 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.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.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.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.focusCellBackground #282828 HSL 0 0 16 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellForeground #dddddd HSL 0 0 87 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 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.foreground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
Table.gridColor #3c3c3c HSL 0 0 24 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.intercellSpacing 0,0 javax.swing.plaf.DimensionUIResource [UI]
Table.paintOutsideAlternateRows false
Table.rowHeight 20 Table.rowHeight 20
Table.scrollPaneBorder [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatScrollPaneBorder [UI] 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] 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.inactiveForeground #9a9a9a HSL 0 0 60 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40 TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12 TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 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.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.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.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.focusCellBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellForeground #262626 HSL 0 0 15 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 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.foreground #262626 HSL 0 0 15 javax.swing.plaf.ColorUIResource [UI]
Table.gridColor #ebebeb HSL 0 0 92 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.intercellSpacing 0,0 javax.swing.plaf.DimensionUIResource [UI]
Table.paintOutsideAlternateRows false
Table.rowHeight 20 Table.rowHeight 20
Table.scrollPaneBorder [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatScrollPaneBorder [UI] 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] 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.inactiveForeground #b6b6b6 HSL 0 0 71 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40 TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12 TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 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.dropCellForeground #00ff00 HSL 120 100 50 javax.swing.plaf.ColorUIResource [UI]
Table.dropLineColor #0000ff HSL 240 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.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.focusCellBackground #fffff0 HSL 60 100 97 javax.swing.plaf.ColorUIResource [UI]
Table.focusCellForeground #ff0000 HSL 0 100 50 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 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.foreground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Table.gridColor #00ffff HSL 180 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.intercellSpacing 0,0 javax.swing.plaf.DimensionUIResource [UI]
Table.paintOutsideAlternateRows false
Table.rowHeight 25 Table.rowHeight 25
Table.scrollPaneBorder [lazy] 1,9,1,9 false com.formdev.flatlaf.ui.FlatScrollPaneBorder [UI] 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] 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.inactiveForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40 TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12 TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 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.radiance.theming.api.skin.RadianceBusinessLookAndFeel = Radiance Business;ctrl F5;org.pushing-pixels:radiance-theming:7.0.1
org.pushingpixels.substance.api.skin.SubstanceGraphiteAquaLookAndFeel = Substance Graphite Aqua;ctrl F6 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.13 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.plastic.PlasticLookAndFeel = JGoodies Looks Plastic;ctrl F12;com.jgoodies:jgoodies-looks:2.7.0
com.jgoodies.looks.windows.WindowsLookAndFeel = JGoodies Looks Windows;ctrl F9 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

@@ -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 width = getWidth();
int height = getHeight(); int height = getHeight();
g.setColor( super.getBackground() ); if( isOpaque() ) {
g.fillRect( 0, 0, width, height ); g.setColor( super.getBackground() );
g.fillRect( 0, 0, width, height );
}
if( isPaintBackgroundPattern() ) { if( isPaintBackgroundPattern() ) {
g.setColor( Color.magenta ); g.setColor( Color.magenta );

View File

@@ -41,6 +41,9 @@ import net.miginfocom.swing.*;
public class FlatWindowDecorationsTest public class FlatWindowDecorationsTest
extends FlatTestPanel extends FlatTestPanel
{ {
// same as in FlatTitlePane
private static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles";
public static void main( String[] args ) { public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> { SwingUtilities.invokeLater( () -> {
if( SystemInfo.isLinux ) { if( SystemInfo.isLinux ) {
@@ -51,7 +54,7 @@ public class FlatWindowDecorationsTest
FlatTestFrame frame = FlatTestFrame.create( args, "FlatWindowDecorationsTest" ); FlatTestFrame frame = FlatTestFrame.create( args, "FlatWindowDecorationsTest" );
frame.applyComponentOrientationToFrame = true; frame.applyComponentOrientationToFrame = true;
UIManager.put( "FlatLaf.debug.titlebar.showRectangles", true ); UIManager.put( KEY_DEBUG_SHOW_RECTANGLES, true );
Class<?> cls = FlatWindowDecorationsTest.class; Class<?> cls = FlatWindowDecorationsTest.class;
List<Image> images = Arrays.asList( List<Image> images = Arrays.asList(
@@ -76,6 +79,14 @@ public class FlatWindowDecorationsTest
initComponents(); initComponents();
} }
@Override
public void updateUI() {
super.updateUI();
if( translucentWindowBackgroundCheckBox != null )
translucentWindowBackgroundChanged();
}
@Override @Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();
@@ -109,6 +120,14 @@ public class FlatWindowDecorationsTest
rootPane.addPropertyChangeListener( "windowDecorationStyle", e -> { rootPane.addPropertyChangeListener( "windowDecorationStyle", e -> {
updateDecorationStyleRadioButtons( rootPane ); 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() { private void addMenu() {
JMenu menu = new JMenu( "Hello" ); JMenu menu = new JMenu( "Hello" );
menu.add( new JMenuItem( "world" ) ); menu.add( new JMenuItem( "world" ) );
@@ -280,12 +320,21 @@ debug*/
JLabel caption = new JLabel( "Caption" ); JLabel caption = new JLabel( "Caption" );
caption.setBackground( Color.green ); caption.setBackground( Color.green );
caption.setOpaque( true ); caption.setOpaque( true );
caption.putClientProperty( FlatClientProperties.COMPONENT_TITLE_BAR_CAPTION, true );
menuBar.add( caption ); menuBar.add( caption );
menuBar.revalidate(); 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() { private void removeMenu() {
int menuCount = menuBar.getMenuCount(); int menuCount = menuBar.getMenuCount();
if( menuCount <= 0 ) if( menuCount <= 0 )
@@ -486,13 +535,31 @@ debug*/
rootPane.putClientProperty( FlatClientProperties.TITLE_BAR_SHOW_CLOSE, showCloseCheckBox.isSelected() ? null : false ); 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() { private JRootPane getWindowRootPane() {
Window window = SwingUtilities.windowForComponent( this ); Window window = SwingUtilities.windowForComponent( this );
if( window instanceof JFrame ) return (window instanceof RootPaneContainer)
return ((JFrame)window).getRootPane(); ? ((RootPaneContainer)window).getRootPane()
else if( window instanceof JDialog ) : null;
return ((JDialog)window).getRootPane(); }
return 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() { private void initComponents() {
@@ -509,6 +576,9 @@ debug*/
showIconifyCheckBox = new JCheckBox(); showIconifyCheckBox = new JCheckBox();
showMaximizeCheckBox = new JCheckBox(); showMaximizeCheckBox = new JCheckBox();
showCloseCheckBox = new JCheckBox(); showCloseCheckBox = new JCheckBox();
fullWindowContentCheckBox = new JCheckBox();
JLabel fullWindowContentButtonsBoundsLabel = new JLabel();
fullWindowContentButtonsBoundsField = new JLabel();
JPanel panel6 = new JPanel(); JPanel panel6 = new JPanel();
menuBarCheckBox = new JCheckBox(); menuBarCheckBox = new JCheckBox();
menuBarEmbeddedCheckBox = new JCheckBox(); menuBarEmbeddedCheckBox = new JCheckBox();
@@ -519,6 +589,7 @@ debug*/
addMenuButton = new JButton(); addMenuButton = new JButton();
addGlueButton = new JButton(); addGlueButton = new JButton();
addCaptionButton = new JButton(); addCaptionButton = new JButton();
addTextFieldButton = new JButton();
removeMenuButton = new JButton(); removeMenuButton = new JButton();
changeMenuButton = new JButton(); changeMenuButton = new JButton();
changeTitleButton = new JButton(); changeTitleButton = new JButton();
@@ -543,11 +614,13 @@ debug*/
colorizeTitleBarCheckBox = new JCheckBox(); colorizeTitleBarCheckBox = new JCheckBox();
colorizeMenuBarCheckBox = new JCheckBox(); colorizeMenuBarCheckBox = new JCheckBox();
colorizeMenusCheckBox = new JCheckBox(); colorizeMenusCheckBox = new JCheckBox();
translucentWindowBackgroundCheckBox = new JCheckBox();
JButton openDialogButton = new JButton(); JButton openDialogButton = new JButton();
JButton openFrameButton = new JButton(); JButton openFrameButton = new JButton();
typeNormalRadioButton = new JRadioButton(); typeNormalRadioButton = new JRadioButton();
typeUtilityRadioButton = new JRadioButton(); typeUtilityRadioButton = new JRadioButton();
typeSmallRadioButton = new JRadioButton(); typeSmallRadioButton = new JRadioButton();
showRectanglesCheckBox = new JCheckBox();
menuBar = new JMenuBar(); menuBar = new JMenuBar();
JMenu fileMenu = new JMenu(); JMenu fileMenu = new JMenu();
JMenuItem newMenuItem = new JMenuItem(); JMenuItem newMenuItem = new JMenuItem();
@@ -586,6 +659,7 @@ debug*/
// rows // rows
"[fill]" + "[fill]" +
"[fill]" + "[fill]" +
"[]" +
"[]")); "[]"));
//======== panel7 ======== //======== panel7 ========
@@ -643,6 +717,8 @@ debug*/
"[]" + "[]" +
"[]" + "[]" +
"[]" + "[]" +
"[]rel" +
"[]rel" +
"[]")); "[]"));
//---- showIconCheckBox ---- //---- showIconCheckBox ----
@@ -673,6 +749,19 @@ debug*/
showCloseCheckBox.setSelected(true); showCloseCheckBox.setSelected(true);
showCloseCheckBox.addActionListener(e -> showCloseChanged()); showCloseCheckBox.addActionListener(e -> showCloseChanged());
panel4.add(showCloseCheckBox, "cell 0 4"); 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"); add(panel4, "cell 1 0");
@@ -731,6 +820,7 @@ debug*/
"[]" + "[]" +
"[]" + "[]" +
"[]" + "[]" +
"[]" +
"[]unrel" + "[]unrel" +
"[]")); "[]"));
@@ -749,20 +839,25 @@ debug*/
addCaptionButton.addActionListener(e -> addCaption()); addCaptionButton.addActionListener(e -> addCaption());
panel3.add(addCaptionButton, "cell 0 2"); panel3.add(addCaptionButton, "cell 0 2");
//---- addTextFieldButton ----
addTextFieldButton.setText("Add text field");
addTextFieldButton.addActionListener(e -> addTextField());
panel3.add(addTextFieldButton, "cell 0 3");
//---- removeMenuButton ---- //---- removeMenuButton ----
removeMenuButton.setText("Remove menu"); removeMenuButton.setText("Remove menu");
removeMenuButton.addActionListener(e -> removeMenu()); removeMenuButton.addActionListener(e -> removeMenu());
panel3.add(removeMenuButton, "cell 0 3"); panel3.add(removeMenuButton, "cell 0 4");
//---- changeMenuButton ---- //---- changeMenuButton ----
changeMenuButton.setText("Change menu"); changeMenuButton.setText("Change menu");
changeMenuButton.addActionListener(e -> changeMenu()); changeMenuButton.addActionListener(e -> changeMenu());
panel3.add(changeMenuButton, "cell 0 4"); panel3.add(changeMenuButton, "cell 0 5");
//---- changeTitleButton ---- //---- changeTitleButton ----
changeTitleButton.setText("Change title"); changeTitleButton.setText("Change title");
changeTitleButton.addActionListener(e -> changeTitle()); 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"); add(panel3, "cell 3 0 1 2,aligny top,growy 0");
@@ -885,6 +980,7 @@ debug*/
"[]" + "[]" +
"[]" + "[]" +
"[]" + "[]" +
"[]para" +
"[]")); "[]"));
//---- unifiedBackgroundCheckBox ---- //---- unifiedBackgroundCheckBox ----
@@ -906,6 +1002,11 @@ debug*/
colorizeMenusCheckBox.setText("colorize menus"); colorizeMenusCheckBox.setText("colorize menus");
colorizeMenusCheckBox.addActionListener(e -> colorizeMenus()); colorizeMenusCheckBox.addActionListener(e -> colorizeMenus());
panel5.add(colorizeMenusCheckBox, "cell 0 3"); 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"); add(panel5, "cell 2 1");
@@ -933,6 +1034,12 @@ debug*/
typeSmallRadioButton.setText("Small"); typeSmallRadioButton.setText("Small");
add(typeSmallRadioButton, "cell 0 2 3 1"); 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 ======== //======== menuBar ========
{ {
@@ -1140,6 +1247,8 @@ debug*/
private JCheckBox showIconifyCheckBox; private JCheckBox showIconifyCheckBox;
private JCheckBox showMaximizeCheckBox; private JCheckBox showMaximizeCheckBox;
private JCheckBox showCloseCheckBox; private JCheckBox showCloseCheckBox;
private JCheckBox fullWindowContentCheckBox;
private JLabel fullWindowContentButtonsBoundsField;
private JCheckBox menuBarCheckBox; private JCheckBox menuBarCheckBox;
private JCheckBox menuBarEmbeddedCheckBox; private JCheckBox menuBarEmbeddedCheckBox;
private JCheckBox menuBarVisibleCheckBox; private JCheckBox menuBarVisibleCheckBox;
@@ -1148,6 +1257,7 @@ debug*/
private JButton addMenuButton; private JButton addMenuButton;
private JButton addGlueButton; private JButton addGlueButton;
private JButton addCaptionButton; private JButton addCaptionButton;
private JButton addTextFieldButton;
private JButton removeMenuButton; private JButton removeMenuButton;
private JButton changeMenuButton; private JButton changeMenuButton;
private JButton changeTitleButton; private JButton changeTitleButton;
@@ -1169,9 +1279,11 @@ debug*/
private JCheckBox colorizeTitleBarCheckBox; private JCheckBox colorizeTitleBarCheckBox;
private JCheckBox colorizeMenuBarCheckBox; private JCheckBox colorizeMenuBarCheckBox;
private JCheckBox colorizeMenusCheckBox; private JCheckBox colorizeMenusCheckBox;
private JCheckBox translucentWindowBackgroundCheckBox;
private JRadioButton typeNormalRadioButton; private JRadioButton typeNormalRadioButton;
private JRadioButton typeUtilityRadioButton; private JRadioButton typeUtilityRadioButton;
private JRadioButton typeSmallRadioButton; private JRadioButton typeSmallRadioButton;
private JCheckBox showRectanglesCheckBox;
private JMenuBar menuBar; private JMenuBar menuBar;
// JFormDesigner - End of variables declaration //GEN-END:variables // 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 { new FormModel {
contentType: "form/swing" contentType: "form/swing"
@@ -9,7 +9,7 @@ new FormModel {
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3" "$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[left][fill][fill][fill]" "$columnConstraints": "[left][fill][fill][fill]"
"$rowConstraints": "[fill][fill][]" "$rowConstraints": "[fill][fill][][]"
} ) { } ) {
name: "this" name: "this"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
@@ -77,7 +77,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,hidemode 3,gap 0 0" "$layoutConstraints": "ltr,hidemode 3,gap 0 0"
"$columnConstraints": "[grow,left]" "$columnConstraints": "[grow,left]"
"$rowConstraints": "[][][][][]" "$rowConstraints": "[][][][][]rel[]rel[]"
} ) { } ) {
name: "panel4" name: "panel4"
"border": new javax.swing.border.TitledBorder( "Title Bar" ) "border": new javax.swing.border.TitledBorder( "Title Bar" )
@@ -135,6 +135,31 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4" "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 ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0" "value": "cell 1 0"
} ) } )
@@ -204,7 +229,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3" "$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill]" "$columnConstraints": "[fill]"
"$rowConstraints": "[][][][][]unrel[]" "$rowConstraints": "[][][][][][]unrel[]"
} ) { } ) {
name: "panel3" name: "panel3"
add( new FormComponent( "javax.swing.JButton" ) { add( new FormComponent( "javax.swing.JButton" ) {
@@ -237,6 +262,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2" "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" ) { add( new FormComponent( "javax.swing.JButton" ) {
name: "removeMenuButton" name: "removeMenuButton"
"text": "Remove menu" "text": "Remove menu"
@@ -245,7 +280,7 @@ new FormModel {
} }
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "removeMenu", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "removeMenu", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3" "value": "cell 0 4"
} ) } )
add( new FormComponent( "javax.swing.JButton" ) { add( new FormComponent( "javax.swing.JButton" ) {
name: "changeMenuButton" name: "changeMenuButton"
@@ -255,7 +290,7 @@ new FormModel {
} }
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeMenu", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeMenu", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4" "value": "cell 0 5"
} ) } )
add( new FormComponent( "javax.swing.JButton" ) { add( new FormComponent( "javax.swing.JButton" ) {
name: "changeTitleButton" name: "changeTitleButton"
@@ -265,7 +300,7 @@ new FormModel {
} }
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeTitle", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeTitle", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5" "value": "cell 0 6"
} ) } )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 0 1 2,aligny top,growy 0" "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 ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,hidemode 3,gap 0 0" "$layoutConstraints": "ltr,hidemode 3,gap 0 0"
"$columnConstraints": "[left]" "$columnConstraints": "[left]"
"$rowConstraints": "[][][][]" "$rowConstraints": "[][][][]para[]"
} ) { } ) {
name: "panel5" name: "panel5"
"border": new javax.swing.border.TitledBorder( "Color" ) "border": new javax.swing.border.TitledBorder( "Color" )
@@ -493,6 +528,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3" "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 ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1" "value": "cell 2 1"
} ) } )
@@ -542,6 +587,17 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2 3 1" "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 ) { }, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 ) "location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 960, 495 ) "size": new java.awt.Dimension( 960, 495 )

View File

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

View File

@@ -68,6 +68,20 @@ public class UIDefaultsKeysDump
collectKeys( FlatDarculaLaf.class.getName(), keys ); collectKeys( FlatDarculaLaf.class.getName(), keys );
collectKeys( FlatTestLaf.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 // write key file
try( Writer fileWriter = new BufferedWriter( new OutputStreamWriter( try( Writer fileWriter = new BufferedWriter( new OutputStreamWriter(
new FileOutputStream( keysFile ), StandardCharsets.UTF_8 ) ) ) new FileOutputStream( keysFile ), StandardCharsets.UTF_8 ) ) )

View File

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

View File

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