Compare commits

..

23 Commits
3.4 ... 3.4.1

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

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

Windows and macOS binaries built and signed locally in clean workspace
Linux binary built by GitHub Actions
2024-03-24 16:37:11 +01:00
Karl Tauber
4e1f092b98 FlatClientProperties: javadoc fixes 2024-03-24 12:45:48 +01:00
Karl Tauber
bd60a18ff4 SplitPane:
- update divider when client property `JSplitPane.expandableSide` changed
- Extras: added support for `JSplitPane.expandableSide` client property to `FlatSplitPane`
2024-03-24 12:42:39 +01:00
Karl Tauber
3b3d7d76eb Testing: added tab with random background color to FlatContainerTest and FlatJideOssContainerTest 2024-03-23 16:33:27 +01:00
Karl Tauber
ec76448e9f jsvg: updated to 1.4.0
jide-oss: updated to 3.7.15
2024-03-23 15:31:45 +01:00
Karl Tauber
872c84974c Merge PR #822: Request to add MCreator to list of apps using FlatLAF 2024-03-23 15:08:20 +01:00
Karl Tauber
5dd2008969 GitHub Actions: build using Java 22 (use toolchain because Gradle 8.7 does not support running on Java 22) 2024-03-23 14:13:43 +01:00
Karl Tauber
55ddac2bc7 Gradle: use simpler Kotlin DSL property assignment
https://blog.gradle.org/simpler-kotlin-dsl-property-assignment
2024-03-23 13:56:47 +01:00
Karl Tauber
a62dd22f83 Gradle: fixed warning regarding using deprecation Gradle features, making it incompatible with Gradle 9.0 2024-03-23 13:46:51 +01:00
Karl Tauber
a503879858 update to Gradle 8.7 2024-03-23 13:10:58 +01:00
Klemen
14f19dd735 Update README.md 2024-03-18 09:07:56 +01:00
Karl Tauber
9a727f68ce Window decorations: fixed missing window top border on Windows 10 in "full window content" mode (issue #809) 2024-03-17 18:09:19 +01:00
Karl Tauber
d26819d268 TabbedPane:
- updated comment regarding unsupported scroll buttons in right-to-left component orientation (see issue #815)
- removed unused on wrong code that tries to layout scroll buttons for right-to-left
2024-03-13 00:03:05 +01:00
Karl Tauber
44752cc9aa TabbedPane: fixed swapped back and forward scroll buttons when using TabbedPane.scrollButtonsPlacement = trailing (regression in FlatLaf 3.3 since commit 97495a6093) 2024-03-12 18:41:47 +01:00
Karl Tauber
11e0757387 FlatSVGIcon:
- setColorFilter() now returns `this`
- added method to enable/disable and clear SVGDocument cache
- do not filter red color in `paintSvgError()`
2024-03-12 13:36:56 +01:00
Karl Tauber
1f1ebc3c44 FlatSVGIcon: some additions to PR #817:
- create new `LinearGradientPaint` only if color has changed
- preserve old `LinearGradientPaint.colorSpace` and `transform`
2024-03-12 12:20:31 +01:00
Karl Tauber
4be97b6ea6 Merge PR #817: Add LinearGradient paint in svg color filter 2024-03-11 19:04:47 +01:00
laing raven
3facca5499 add LinearGradient paint in svg color filter 2024-03-10 19:12:29 +07:00
58 changed files with 705 additions and 147 deletions

View File

@@ -32,10 +32,11 @@ jobs:
- 8
- 11 # LTS
- 17 # LTS
- 21 # LTS
toolchain: [""]
include:
- java: 17
toolchain: 21 # latest
- java: 21
toolchain: 22 # latest
steps:
- uses: actions/checkout@v4
@@ -47,7 +48,7 @@ jobs:
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: temurin # Java 8, 11 and 17 are pre-installed on ubuntu-latest
distribution: temurin # Java 8, 11, 17 and 21 are pre-installed on ubuntu-latest
cache: gradle
- name: Check with Error Prone

View File

@@ -1,6 +1,37 @@
FlatLaf Change Log
==================
## 3.4.1
#### Fixed bugs
- SplitPane: Update divider when client property `JSplitPane.expandableSide`
changed.
- TabbedPane: Fixed swapped back and forward scroll buttons when using
`TabbedPane.scrollButtonsPlacement = trailing` (regression in FlatLaf 3.3).
- Fixed missing window top border on Windows 10 in "full window content" mode.
(issue 809)
- Extras:
- `FlatSVGIcon` color filters now support linear gradients. (PR #817)
- `FlatSVGIcon`: Use log level `CONFIG` instead of `SEVERE` and allow
disabling logging. (issue #823)
- Added support for `JSplitPane.expandableSide` client property to
`FlatSplitPane`.
- Native libraries: Added API version check to test whether native library
matches the JAR (bad builds could e.g. ship a newer JAR with an older
incompatible native library) and to test whether native methods can be invoked
(some security software allows loading native library but blocks method
invocation).
- macOS: Fixed crash when running in WebSwing. (issue #826; regression in 3.4)
#### Incompatibilities
- File names of custom properties files for nested Laf classes now must include
name of enclosing class name. E.g. nested Laf class `IntelliJTheme.ThemeLaf`
used `ThemeLaf.properties` in previous versions, but now needs to be named
`IntelliJTheme$ThemeLaf.properties`.
## 3.4
#### New features and improvements

View File

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

View File

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

View File

@@ -44,34 +44,34 @@ publishing {
pom {
afterEvaluate {
this@pom.name.set( extension.name )
this@pom.description.set( extension.description )
this@pom.name = extension.name
this@pom.description = extension.description
}
url.set( "https://github.com/JFormDesigner/FlatLaf" )
url = "https://github.com/JFormDesigner/FlatLaf"
licenses {
license {
name.set( "The Apache License, Version 2.0" )
url.set( "https://www.apache.org/licenses/LICENSE-2.0.txt" )
name = "The Apache License, Version 2.0"
url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
}
}
developers {
developer {
name.set( "Karl Tauber" )
organization.set( "FormDev Software GmbH" )
organizationUrl.set( "https://www.formdev.com/" )
name = "Karl Tauber"
organization = "FormDev Software GmbH"
organizationUrl = "https://www.formdev.com/"
}
}
scm {
connection.set( "scm:git:git://github.com/JFormDesigner/FlatLaf.git" )
url.set( "https://github.com/JFormDesigner/FlatLaf" )
connection = "scm:git:git://github.com/JFormDesigner/FlatLaf.git"
url = "https://github.com/JFormDesigner/FlatLaf"
}
issueManagement {
system.set( "GitHub" )
url.set( "https://github.com/JFormDesigner/FlatLaf/issues" )
system = "GitHub"
url = "https://github.com/JFormDesigner/FlatLaf/issues"
}
}

View File

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

View File

@@ -27,8 +27,8 @@ plugins {
val sigtest = configurations.create( "sigtest" )
dependencies {
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
testImplementation( libs.junit )
testRuntimeOnly( libs.junit.launcher )
// https://github.com/jtulach/netbeans-apitest
sigtest( libs.sigtest )
@@ -42,11 +42,11 @@ java {
tasks {
compileJava {
// generate JNI headers
options.headerOutputDirectory.set( layout.buildDirectory.dir( "generated/jni-headers" ) )
options.headerOutputDirectory = layout.buildDirectory.dir( "generated/jni-headers" )
}
jar {
archiveBaseName.set( "flatlaf" )
archiveBaseName = "flatlaf"
doLast {
ReorderJarEntries.reorderJarEntries( outputs.files.singleFile );
@@ -54,20 +54,20 @@ tasks {
}
named<Jar>( "sourcesJar" ) {
archiveBaseName.set( "flatlaf" )
archiveBaseName = "flatlaf"
}
named<Jar>( "javadocJar" ) {
archiveBaseName.set( "flatlaf" )
archiveBaseName = "flatlaf"
}
register<Zip>( "jarNoNatives" ) {
group = "build"
dependsOn( "jar" )
archiveBaseName.set( "flatlaf" )
archiveClassifier.set( "no-natives" )
archiveExtension.set( "jar" )
archiveBaseName = "flatlaf"
archiveClassifier = "no-natives"
archiveExtension = "jar"
destinationDirectory = layout.buildDirectory.dir( "libs" )
from( zipTree( jar.get().archiveFile.get().asFile ) )

View File

@@ -102,6 +102,17 @@ public interface FlatClientProperties
*/
String BUTTON_TYPE_BORDERLESS = "borderless";
/**
* Specifies whether the button preferred size will be made square (quadratically).
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String SQUARE_SIZE = "JButton.squareSize";
//---- JCheckBox ----------------------------------------------------------
/**
* Specifies selected state of a checkbox.
* <p>
@@ -118,14 +129,6 @@ public interface FlatClientProperties
*/
String SELECTED_STATE_INDETERMINATE = "indeterminate";
/**
* Specifies whether the button preferred size will be made square (quadratically).
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String SQUARE_SIZE = "JButton.squareSize";
//---- JComponent ---------------------------------------------------------
@@ -984,7 +987,7 @@ public interface FlatClientProperties
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link SwingConstants#LEADING} (default)
* {@link SwingConstants#LEADING} (default),
* {@link SwingConstants#TRAILING},
* {@link SwingConstants#CENTER},
* {@link #TABBED_PANE_ALIGN_LEADING} (default),
@@ -1096,7 +1099,7 @@ public interface FlatClientProperties
* <strong>Allowed Values</strong>
* {@link SwingConstants#LEFT},
* {@link SwingConstants#RIGHT},
* {@link #TABBED_PANE_TAB_ROTATION_NONE}, (default)
* {@link #TABBED_PANE_TAB_ROTATION_NONE} (default),
* {@link #TABBED_PANE_TAB_ROTATION_AUTO},
* {@link #TABBED_PANE_TAB_ROTATION_LEFT} or
* {@link #TABBED_PANE_TAB_ROTATION_RIGHT}
@@ -1345,8 +1348,8 @@ public interface FlatClientProperties
* <p>
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br>
* <strong>SupportedValues:</strong>
* {@link SwingConstants#BOTTOM} (default)
* <strong>Allowed Values</strong>
* {@link SwingConstants#BOTTOM} (default),
* {@link SwingConstants#TOP},
* {@link SwingConstants#LEFT} or
* {@link SwingConstants#RIGHT}

View File

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

View File

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

View File

@@ -37,16 +37,18 @@ class FlatNativeLibrary
private static boolean initialized;
private static NativeLibrary nativeLibrary;
private native static int getApiVersion();
/**
* Loads native library (if available) and returns whether loaded successfully.
* Returns {@code false} if no native library is available.
*/
static synchronized boolean isLoaded() {
initialize();
static synchronized boolean isLoaded( int apiVersion ) {
initialize( apiVersion );
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
}
private static void initialize() {
private static void initialize( int apiVersion ) {
if( initialized )
return;
initialized = true;
@@ -104,7 +106,26 @@ class FlatNativeLibrary
return; // no native library available for current OS or CPU architecture
// load native library
nativeLibrary = createNativeLibrary( classifier, ext );
NativeLibrary nativeLibrary = createNativeLibrary( classifier, ext );
if( !nativeLibrary.isLoaded() )
return;
// check API version (and check whether library works)
try {
int actualApiVersion = getApiVersion();
if( actualApiVersion != apiVersion ) {
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Wrong API version in native library (expected "
+ apiVersion + ", actual " + actualApiVersion + "). Ignoring native library.", null );
return;
}
} catch( Throwable ex ) {
// could be a UnsatisfiedLinkError in case that loading native library
// from temp directory was blocked by some OS security mechanism
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to get API version of native library. Ignoring native library.", ex );
return;
}
FlatNativeLibrary.nativeLibrary = nativeLibrary;
}
private static NativeLibrary createNativeLibrary( String classifier, String ext ) {
@@ -118,9 +139,9 @@ class FlatNativeLibrary
if( library.isLoaded() )
return library;
LoggingFacade.INSTANCE.logSevere( "Did not find library '" + System.mapLibraryName( libraryName )
+ "' in java.library.path '" + System.getProperty( "java.library.path" )
+ "', using extracted library instead", null );
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Native library '" + System.mapLibraryName( libraryName )
+ "' not found in java.library.path '" + System.getProperty( "java.library.path" )
+ "'. Using extracted native library instead.", null );
} else {
// try standard library naming scheme
// (same as in flatlaf.jar in package 'com/formdev/flatlaf/natives')
@@ -139,11 +160,11 @@ class FlatNativeLibrary
return new NativeLibrary( libraryFile2, true );
}
LoggingFacade.INSTANCE.logSevere( "Did not find library '"
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Native library '"
+ libraryFile.getName()
+ (libraryName2 != null ? ("' or '" + libraryName2) : "")
+ "' in '" + libraryFile.getParentFile().getAbsolutePath()
+ "', using extracted library instead", null );
+ "' not found in '" + libraryFile.getParentFile().getAbsolutePath()
+ "'. Using extracted native library instead.", null );
}
}

View File

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

View File

@@ -44,6 +44,8 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
public class FlatNativeMacLibrary
{
private static int API_VERSION_MACOS = 2001;
/**
* Checks whether native library is loaded/available.
* <p>
@@ -51,7 +53,7 @@ public class FlatNativeMacLibrary
* method of this class. Otherwise, the native library may not be loaded.
*/
public static boolean isLoaded() {
return SystemInfo.isMacOS && FlatNativeLibrary.isLoaded();
return SystemInfo.isMacOS && FlatNativeLibrary.isLoaded( API_VERSION_MACOS );
}
public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor );

View File

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

View File

@@ -270,6 +270,7 @@ public class FlatRootPaneUI
// layer title pane under frame content layer to allow placing menu bar over title pane
protected final static Integer TITLE_PANE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 1;
private final static Integer TITLE_PANE_MOUSE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 2;
private final static Integer WINDOW_TOP_BORDER_LAYER = Integer.MAX_VALUE;
// for fullWindowContent mode, layer title pane over frame content layer to allow placing title bar buttons over content
/** @since 3.4 */
@@ -285,11 +286,15 @@ public class FlatRootPaneUI
if( titlePane != null ) {
layeredPane.remove( titlePane );
layeredPane.remove( titlePane.mouseLayer );
if( titlePane.windowTopBorderLayer != null )
layeredPane.remove( titlePane.windowTopBorderLayer );
}
if( newTitlePane != null ) {
layeredPane.add( newTitlePane, getLayerForTitlePane() );
layeredPane.add( newTitlePane.mouseLayer, TITLE_PANE_MOUSE_LAYER );
if( newTitlePane.windowTopBorderLayer != null && newTitlePane.isWindowTopBorderNeeded() && isFullWindowContent( rootPane ) )
layeredPane.add( newTitlePane.windowTopBorderLayer, WINDOW_TOP_BORDER_LAYER );
}
titlePane = newTitlePane;
@@ -446,6 +451,13 @@ public class FlatRootPaneUI
case FlatClientProperties.FULL_WINDOW_CONTENT:
if( titlePane != null ) {
rootPane.getLayeredPane().setLayer( titlePane, getLayerForTitlePane() );
if( titlePane.windowTopBorderLayer != null ) {
JLayeredPane layeredPane = rootPane.getLayeredPane();
if( titlePane.isWindowTopBorderNeeded() && isFullWindowContent( rootPane ) )
layeredPane.add( titlePane.windowTopBorderLayer, WINDOW_TOP_BORDER_LAYER );
else
layeredPane.remove( titlePane.windowTopBorderLayer );
}
titlePane.updateIcon();
titlePane.updateVisibility();
titlePane.updateFullWindowContentButtonsBoundsProperty();
@@ -591,6 +603,12 @@ public class FlatRootPaneUI
titlePane.setBounds( 0, 0, width, prefHeight );
titlePane.mouseLayer.setBounds( 0, 0, width, prefHeight );
if( titlePane.windowTopBorderLayer != null ) {
boolean show = isFullWindowContent && !titlePane.isWindowMaximized() && !isFullScreen;
if( show )
titlePane.windowTopBorderLayer.setBounds( 0, 0, width, 1 );
titlePane.windowTopBorderLayer.setVisible( show );
}
if( !isFullWindowContent )
nextY += prefHeight;

View File

@@ -301,6 +301,10 @@ public class FlatSplitPaneUI
// necessary to show/hide one-touch buttons on expand/collapse
doLayout();
break;
case FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE:
revalidate();
break;
}
}

View File

@@ -3767,9 +3767,20 @@ debug*/
boolean hideDisabledScrollButtons = (scrollButtonsPolicy == AS_NEEDED_SINGLE && scrollButtonsPlacement == BOTH);
boolean trailingScrollButtons = (scrollButtonsPlacement == TRAILING);
// for right-to-left always use "more tabs" button for horizontal scrolling
// For right-to-left, always use "more tabs" button for horizontal scrolling
// because methods scrollForward() and scrollBackward() in class
// BasicTabbedPaneUI.ScrollableTabSupport do not work for right-to-left
// BasicTabbedPaneUI.ScrollableTabSupport do not work for right-to-left.
//
// One problem is that BasicTabbedPaneUI.getClosestTab(), which is used
// to compute leadingTabIndex, does not work for right-to-left because is uses "binary" search
// on rects[] to find tab, but rects[] is ordered in reverse order for right-to-left.
// So leadingTabIndex is either zero or tabCount.
// Therefore increasing/decreasing leadingTabIndex in scrollForward()
// and scrollBackward() does not work as expected.
// Also backward/forward scroll buttons are not correctly enabled/disabled.
//
// Fixing this would require replacing nearly whole functionality of class
// BasicTabbedPaneUI.ScrollableTabSupport, which is not possible because it is private.
boolean leftToRight = isLeftToRight();
if( !leftToRight && isHorizontalTabPlacement( tabPane.getTabPlacement() ) ) {
useMoreTabsButton = true;
@@ -3851,6 +3862,8 @@ debug*/
w -= buttonWidth;
moreTabsButtonVisible = true;
}
// layout scroll buttons
if( useScrollButtons ) {
// the tabViewport view size is set in
// BasicTabbedPaneUI.TabbedPaneScrollLayout.calculateTabRects(),
@@ -3858,30 +3871,28 @@ debug*/
Point viewPosition = tabViewport.getViewPosition();
Dimension viewSize = tabViewport.getViewSize();
// layout forward button on trailing side
if( !hideDisabledScrollButtons || viewSize.width - viewPosition.x > w ) {
int buttonWidth = forwardButton.getPreferredSize().width;
forwardButton.setBounds( x + w - buttonWidth, y, buttonWidth, h );
w -= buttonWidth;
forwardButtonVisible = true;
}
// layout backward button
if( !hideDisabledScrollButtons || viewPosition.x > 0 ) {
int buttonWidth = backwardButton.getPreferredSize().width;
if( trailingScrollButtons ) {
// on trailing side
backwardButton.setBounds( leftToRight ? (x + w - buttonWidth) : x, y, buttonWidth, h );
x += leftToRight ? 0 : buttonWidth;
backwardButton.setBounds( x + w - buttonWidth, y, buttonWidth, h );
} else {
// on leading side
backwardButton.setBounds( leftToRight ? x : (x + w - buttonWidth), y, buttonWidth, h );
x += leftToRight ? buttonWidth : 0;
backwardButton.setBounds( x, y, buttonWidth, h );
x += buttonWidth;
}
w -= buttonWidth;
backwardButtonVisible = true;
}
// layout forward button on trailing side
if( !hideDisabledScrollButtons || viewSize.width - viewPosition.x > w ) {
int buttonWidth = forwardButton.getPreferredSize().width;
forwardButton.setBounds( leftToRight ? (x + w - buttonWidth) : x, y, buttonWidth, h );
x += leftToRight ? 0 : buttonWidth;
w -= buttonWidth;
forwardButtonVisible = true;
}
}
}
@@ -3927,6 +3938,8 @@ debug*/
h -= buttonHeight;
moreTabsButtonVisible = true;
}
// layout scroll buttons
if( useScrollButtons ) {
// the tabViewport view size is set in
// BasicTabbedPaneUI.TabbedPaneScrollLayout.calculateTabRects(),
@@ -3934,6 +3947,14 @@ debug*/
Point viewPosition = tabViewport.getViewPosition();
Dimension viewSize = tabViewport.getViewSize();
// layout forward button on bottom side
if( !hideDisabledScrollButtons || viewSize.height - viewPosition.y > h ) {
int buttonHeight = forwardButton.getPreferredSize().height;
forwardButton.setBounds( x, y + h - buttonHeight, w, buttonHeight );
h -= buttonHeight;
forwardButtonVisible = true;
}
// layout backward button
if( !hideDisabledScrollButtons || viewPosition.y > 0 ) {
int buttonHeight = backwardButton.getPreferredSize().height;
@@ -3948,14 +3969,6 @@ debug*/
h -= buttonHeight;
backwardButtonVisible = true;
}
// layout forward button on bottom side
if( !hideDisabledScrollButtons || viewSize.height - viewPosition.y > h ) {
int buttonHeight = forwardButton.getPreferredSize().height;
forwardButton.setBounds( x, y + h - buttonHeight, w, buttonHeight );
h -= buttonHeight;
forwardButtonVisible = true;
}
}
}

View File

@@ -110,6 +110,7 @@ public class FlatTitlePane
extends JComponent
{
static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles";
private static final boolean isWindows_10 = SystemInfo.isWindows_10_orLater && !SystemInfo.isWindows_11_orLater;
/** @since 2.5 */ protected final Font titleFont;
protected final Color activeBackground;
@@ -166,6 +167,16 @@ public class FlatTitlePane
*/
final JPanel mouseLayer;
/**
* This panel paint a border at the top of the window in fullWindowContent mode,
* if FlatLaf window decorations are enabled.
* Only used on Windows 10.
* <p>
* This panel is not a child of the title pane.
* Instead it is added by FlatRootPaneUI to the layered pane at a layer over all other layers.
*/
final JPanel windowTopBorderLayer;
public FlatTitlePane( JRootPane rootPane ) {
this.rootPane = rootPane;
@@ -207,6 +218,14 @@ public class FlatTitlePane
mouseLayer.addMouseListener( handler );
mouseLayer.addMouseMotionListener( handler );
if( isWindows_10 && FlatNativeWindowBorder.isSupported() ) {
windowTopBorderLayer = new JPanel();
windowTopBorderLayer.setVisible( false );
windowTopBorderLayer.setOpaque( false );
windowTopBorderLayer.setBorder( FlatUIUtils.nonUIResource( FlatNativeWindowBorder.WindowTopBorder.getInstance() ) );
} else
windowTopBorderLayer = null;
applyComponentOrientation( rootPane.getComponentOrientation() );
}
@@ -919,6 +938,10 @@ public class FlatTitlePane
return window != null && FlatNativeWindowBorder.hasCustomDecoration( window );
}
boolean isWindowTopBorderNeeded() {
return isWindows_10 && hasNativeCustomDecoration();
}
// used to invoke updateNativeTitleBarHeightAndHitTestSpots() only once from latest invokeLater()
private int laterCounter;
@@ -1146,7 +1169,7 @@ public class FlatTitlePane
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
insets.bottom += UIScale.scale( 1 );
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
if( isWindowTopBorderNeeded() && !isWindowMaximized() )
insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() );
return insets;
@@ -1165,7 +1188,7 @@ public class FlatTitlePane
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
}
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
if( isWindowTopBorderNeeded() && !isWindowMaximized() && !isFullWindowContent() )
WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
}
@@ -1329,7 +1352,7 @@ public class FlatTitlePane
activeChanged( true );
updateNativeTitleBarHeightAndHitTestSpots();
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() )
if( isWindowTopBorderNeeded() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder();
@@ -1340,7 +1363,7 @@ public class FlatTitlePane
activeChanged( false );
updateNativeTitleBarHeightAndHitTestSpots();
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() )
if( isWindowTopBorderNeeded() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder();

View File

@@ -89,7 +89,7 @@ class FlatWindowsNativeWindowBorder
return null;
// check whether native library was successfully loaded
if( !FlatNativeLibrary.isLoaded() )
if( !FlatNativeWindowsLibrary.isLoaded() )
return null;
// create new instance

View File

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

View File

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

View File

@@ -33,8 +33,8 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
testImplementation( libs.junit )
testRuntimeOnly( libs.junit.launcher )
}
flatlafModuleInfo {
@@ -73,8 +73,8 @@ publishing {
pom {
licenses {
license {
name.set( "SIL OPEN FONT LICENSE Version 1.1" )
url.set( "https://choosealicense.com/licenses/ofl-1.1/" )
name = "SIL OPEN FONT LICENSE Version 1.1"
url = "https://choosealicense.com/licenses/ofl-1.1/"
}
}
}

View File

@@ -33,8 +33,8 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
testImplementation( libs.junit )
testRuntimeOnly( libs.junit.launcher )
}
flatlafModuleInfo {
@@ -73,8 +73,8 @@ publishing {
pom {
licenses {
license {
name.set( "SIL OPEN FONT LICENSE Version 1.1" )
url.set( "https://choosealicense.com/licenses/ofl-1.1/" )
name = "SIL OPEN FONT LICENSE Version 1.1"
url = "https://choosealicense.com/licenses/ofl-1.1/"
}
}
}

View File

@@ -33,8 +33,8 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
testImplementation( libs.junit )
testRuntimeOnly( libs.junit.launcher )
}
flatlafModuleInfo {

View File

@@ -33,8 +33,8 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
testImplementation( libs.junit )
testRuntimeOnly( libs.junit.launcher )
}
flatlafModuleInfo {

View File

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

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2024 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include "com_formdev_flatlaf_ui_FlatNativeLibrary.h"
/**
* @author Karl Tauber
*/
// increase this version if changing API or functionality of native library
// also update version in Java class com.formdev.flatlaf.ui.FlatNativeLinuxLibrary
#define API_VERSION_LINUX 3001
//---- JNI methods ------------------------------------------------------------
extern "C"
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLibrary_getApiVersion
( JNIEnv* env, jclass cls )
{
return API_VERSION_LINUX;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2024 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include "com_formdev_flatlaf_ui_FlatNativeLibrary.h"
/**
* @author Karl Tauber
*/
// increase this version if changing API or functionality of native library
// also update version in Java class com.formdev.flatlaf.ui.FlatNativeMacLibrary
#define API_VERSION_MACOS 2001
//---- JNI methods ------------------------------------------------------------
extern "C"
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLibrary_getApiVersion
( JNIEnv* env, jclass cls )
{
return API_VERSION_MACOS;
}

View File

@@ -38,10 +38,9 @@ jclass findClass( JNIEnv *env, const char* className, bool globalRef ) {
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 );
jfieldID getFieldID( JNIEnv *env, jclass cls, const char* fieldName, const char* fieldSignature, bool staticField ) {
// NSLog( @"getFieldID %s %s", fieldName, fieldSignature );
jclass cls = findClass( env, className, false );
if( cls == NULL )
return NULL;
@@ -49,7 +48,7 @@ jfieldID getFieldID( JNIEnv *env, const char* className, const char* fieldName,
? env->GetStaticFieldID( cls, fieldName, fieldSignature )
: env->GetFieldID( cls, fieldName, fieldSignature );
if( fieldID == NULL ) {
NSLog( @"FlatLaf: failed to lookup field '%s' of type '%s' in class '%s'", fieldName, fieldSignature, className );
NSLog( @"FlatLaf: failed to lookup field '%s' of type '%s'", fieldName, fieldSignature );
env->ExceptionDescribe(); // print stack trace
env->ExceptionClear();
return NULL;

View File

@@ -52,21 +52,27 @@ NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ) {
if( window == NULL )
return NULL;
// initialize class IDs (done only once because variables are static)
static jclass lwWindowPeerClass = findClass( env, "sun/lwawt/LWWindowPeer", true );
static jclass cfRetainedResourceClass = findClass( env, "sun/lwawt/macosx/CFRetainedResource", true );
if( lwWindowPeerClass == NULL || cfRetainedResourceClass == NULL )
return NULL;
// initialize field IDs (done only once because variables are static)
static jfieldID peerID = getFieldID( env, "java/awt/Component", "peer", "Ljava/awt/peer/ComponentPeer;", false );
static jfieldID platformWindowID = getFieldID( env, "sun/lwawt/LWWindowPeer", "platformWindow", "Lsun/lwawt/PlatformWindow;", false );
static jfieldID ptrID = getFieldID( env, "sun/lwawt/macosx/CFRetainedResource", "ptr", "J", false );
static jfieldID peerID = getFieldID( env, findClass( env, "java/awt/Component", false ), "peer", "Ljava/awt/peer/ComponentPeer;", false );
static jfieldID platformWindowID = getFieldID( env, lwWindowPeerClass, "platformWindow", "Lsun/lwawt/PlatformWindow;", false );
static jfieldID ptrID = getFieldID( env, cfRetainedResourceClass, "ptr", "J", false );
if( peerID == NULL || platformWindowID == NULL || ptrID == NULL )
return NULL;
// get field java.awt.Component.peer
jobject peer = env->GetObjectField( window, peerID );
if( peer == NULL )
if( peer == NULL || !env->IsInstanceOf( peer, lwWindowPeerClass ) )
return NULL;
// get field sun.lwawt.LWWindowPeer.platformWindow
jobject platformWindow = env->GetObjectField( peer, platformWindowID );
if( platformWindow == NULL )
if( platformWindow == NULL || !env->IsInstanceOf( platformWindow, cfRetainedResourceClass ) )
return NULL;
// get field sun.lwawt.macosx.CFRetainedResource.ptr

View File

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

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2024 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include "com_formdev_flatlaf_ui_FlatNativeLibrary.h"
/**
* @author Karl Tauber
*/
// increase this version if changing API or functionality of native library
// also update version in Java class com.formdev.flatlaf.ui.FlatNativeWindowsLibrary
#define API_VERSION_WINDOWS 1001
//---- JNI methods ------------------------------------------------------------
extern "C"
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLibrary_getApiVersion
( JNIEnv* env, jclass cls )
{
return API_VERSION_WINDOWS;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

After

Width:  |  Height:  |  Size: 417 B

View File

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

After

Width:  |  Height:  |  Size: 336 B

View File

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

After

Width:  |  Height:  |  Size: 336 B

View File

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

After

Width:  |  Height:  |  Size: 767 B

View File

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

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -14,7 +14,7 @@
# limitations under the License.
#
flatlaf.releaseVersion = 3.4
flatlaf.releaseVersion = 3.4.1
flatlaf.developmentVersion = 3.5-SNAPSHOT
org.gradle.parallel = true

View File

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

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

20
gradlew.bat vendored
View File

@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail