diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60a2ce19..1f2e6e13 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,7 @@ jobs: needs: build if: | github.event_name == 'push' && - github.ref == 'refs/heads/main' && + (github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/heads/develop-' )) && github.repository == 'JFormDesigner/FlatLaf' steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index c4e46b71..95156014 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,25 @@ FlatLaf Change Log - `FlatSVGUtils`: Support loading SVG from `URL` (for JPMS). (issue #325) +## 1.6.5 + +#### Fixed bugs + +- Linux: Fixed font problems when running on Oracle Java (OpenJDK is not + affected): + - oversized text if system font is "Inter" (issue #427) + - missing text if system font is "Cantarell" (on Fedora) +- MenuItem: Changed accelerator delimiter from `-` to `+`. (Windows and Linux). +- ComboBox: Fixed occasional `StackOverflowError` when modifying combo box not + on AWT thread. (issue #432) +- macOS: Fixed `NullPointerException` when using AWT component + `java.awt.Choice`. (issue #439) +- Native window decorations: Do not exit application with `UnsatisfiedLinkError` + in case that FlatLaf DLL cannot be executed because of restrictions on + temporary directory. Instead, continue with default window decorations. (issue + #436) + + ## 1.6.4 #### Fixed bugs diff --git a/build.gradle.kts b/build.gradle.kts index 5857ac40..00f33f6a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,7 +14,7 @@ * limitations under the License. */ -val releaseVersion = "1.6.4" +val releaseVersion = "1.6.5" val developmentVersion = "2.0-SNAPSHOT" version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java index af3ef041..592d1aa2 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java @@ -52,6 +52,7 @@ import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; +import javax.swing.JMenuBar; import javax.swing.LookAndFeel; import javax.swing.PopupFactory; import javax.swing.RootPaneContainer; @@ -1036,12 +1037,23 @@ public abstract class FlatLaf /** * Revalidate and repaint all displayable frames and dialogs. + *
+ * Useful to update UI after changing {@code TitlePane.menuBarEmbedded}. * * @since 1.1.2 */ public static void revalidateAndRepaintAllFramesAndDialogs() { for( Window w : Window.getWindows() ) { if( isDisplayableFrameOrDialog( w ) ) { + // revalidate menu bar + JMenuBar menuBar = (w instanceof JFrame) + ? ((JFrame)w).getJMenuBar() + : (w instanceof JDialog + ? ((JDialog)w).getJMenuBar() + : null); + if( menuBar != null ) + menuBar.revalidate(); + w.revalidate(); w.repaint(); } @@ -1050,6 +1062,9 @@ public abstract class FlatLaf /** * Repaint all displayable frames and dialogs. + *
+ * Useful to update UI after changing {@code TitlePane.unifiedBackground},
+ * {@code MenuItem.selectionType} or {@code Component.hideMnemonics}.
*
* @since 1.1.2
*/
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/LinuxFontPolicy.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/LinuxFontPolicy.java
index b20fba54..837f5052 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/LinuxFontPolicy.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/LinuxFontPolicy.java
@@ -17,6 +17,7 @@
package com.formdev.flatlaf;
import java.awt.Font;
+import java.awt.FontMetrics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
@@ -28,7 +29,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
-
+import javax.swing.text.StyleContext;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
@@ -121,14 +122,25 @@ class LinuxFontPolicy
for(;;) {
Font font = createFont( family, style, size, dsize );
- // if the font family does not match any font on the system, "Dialog" family is returned
- if( !"Dialog".equals( font.getFamily() ) || "Dialog".equals( family ) )
+ if( Font.DIALOG.equals( family ) )
return font;
+ // if the font family does not match any font on the system, "Dialog" family is returned
+ if( !Font.DIALOG.equals( font.getFamily() ) ) {
+ // check for font problems
+ // - font height much larger than expected (e.g. font Inter; Oracle Java 8)
+ // - character width is zero (e.g. font Cantarell; Fedora; Oracle Java 8)
+ FontMetrics fm = StyleContext.getDefaultStyleContext().getFontMetrics( font );
+ if( fm.getHeight() > size * 2 || fm.stringWidth( "a" ) == 0 )
+ return createFont( Font.DIALOG, style, size, dsize );
+
+ return font;
+ }
+
// find last word in family
int index = family.lastIndexOf( ' ' );
if( index < 0 )
- return createFont( "Dialog", style, size, dsize );
+ return createFont( Font.DIALOG, style, size, dsize );
// check whether last work contains some font weight (e.g. Ultra-Bold or Heavy)
String lastWord = family.substring( index + 1 ).toLowerCase();
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java
index 37639d94..d2e160ac 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java
@@ -281,7 +281,10 @@ public class FlatComboBoxUI
public void layoutContainer( Container parent ) {
super.layoutContainer( parent );
- if( arrowButton != null ) {
+ // on macOS, a Swing combo box is used for AWT component java.awt.Choice
+ // and the font may be (temporary) null
+
+ if( arrowButton != null && comboBox.getFont() != null ) {
// limit button width to height of a raw combobox (without insets)
FontMetrics fm = comboBox.getFontMetrics( comboBox.getFont() );
int maxButtonWidth = fm.getHeight() + scale( padding.top ) + scale( padding.bottom );
@@ -908,7 +911,9 @@ public class FlatComboBoxUI
this.padding = padding;
}
- void install( Component c ) {
+ // using synchronized to avoid problems with code that modifies combo box
+ // (model, selection, etc) not on AWT thread (which should be not done)
+ synchronized void install( Component c ) {
if( !(c instanceof JComponent) )
return;
@@ -940,7 +945,7 @@ public class FlatComboBoxUI
* there is no single place to uninstall it.
* This is the reason why this method is called from various places.
*/
- void uninstall() {
+ synchronized void uninstall() {
if( rendererComponent == null )
return;
@@ -951,9 +956,9 @@ public class FlatComboBoxUI
}
@Override
- public Insets getBorderInsets( Component c, Insets insets ) {
+ synchronized public Insets getBorderInsets( Component c, Insets insets ) {
Insets padding = scale( this.padding );
- if( rendererBorder != null ) {
+ if( rendererBorder != null && !(rendererBorder instanceof CellPaddingBorder) ) {
Insets insideInsets = rendererBorder.getBorderInsets( c );
insets.top = Math.max( padding.top, insideInsets.top );
insets.left = Math.max( padding.left, insideInsets.left );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupFactory.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupFactory.java
index 94c65fa9..a7178ea5 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupFactory.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupFactory.java
@@ -121,6 +121,10 @@ public class FlatPopupFactory
popupWindow.getGraphicsConfiguration() == owner.getGraphicsConfiguration() )
return popup;
+ // avoid endless loop (should newer happen; PopupFactory cache size is 5)
+ if( ++count > 10 )
+ return popup;
+
// remove contents component from popup window
if( popupWindow instanceof JWindow )
((JWindow)popupWindow).getContentPane().removeAll();
@@ -128,10 +132,6 @@ public class FlatPopupFactory
// dispose unused popup
// (do not invoke popup.hide() because this would cache the popup window)
popupWindow.dispose();
-
- // avoid endless loop (should newer happen; PopupFactory cache size is 5)
- if( ++count > 10 )
- return popup;
}
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowsNativeWindowBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowsNativeWindowBorder.java
index 2f76e6f5..62f21452 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowsNativeWindowBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowsNativeWindowBorder.java
@@ -167,11 +167,18 @@ class FlatWindowsNativeWindowBorder
return;
// install
- WndProc wndProc = new WndProc( window );
- if( wndProc.hwnd == 0 )
- return;
+ try {
+ WndProc wndProc = new WndProc( window );
+ if( wndProc.hwnd == 0 )
+ return;
- windowsMap.put( window, wndProc );
+ windowsMap.put( window, wndProc );
+ } catch( UnsatisfiedLinkError ex ) {
+ // catch for the case that the operating system prevents execution of DLL
+ // (e.g. if DLLs in temp folder are restricted)
+ // --> continue application without custom decorations
+ LoggingFacade.INSTANCE.logSevere( null, ex );
+ }
}
private void uninstall( Window window ) {
diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
index 3392a4f9..b8e79354 100644
--- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
+++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
@@ -439,7 +439,7 @@ MenuItem.iconTextGap = 6
MenuItem.textAcceleratorGap = 24
MenuItem.textNoAcceleratorGap = 6
MenuItem.acceleratorArrowGap = 2
-MenuItem.acceleratorDelimiter = -
+MenuItem.acceleratorDelimiter = +
[mac]MenuItem.acceleratorDelimiter =
# for MenuItem.selectionType = underline
diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatAWTTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatAWTTest.java
new file mode 100644
index 00000000..2a9691f1
--- /dev/null
+++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatAWTTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2021 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 java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import com.formdev.flatlaf.FlatLightLaf;
+
+/**
+ * Used to test AWT components on macOS, which internally use Swing.
+ *
+ * @author Karl Tauber
+ */
+public class FlatAWTTest
+{
+ public static void main( String[] args ) {
+ EventQueue.invokeLater( () -> {
+ FlatLightLaf.setup();
+
+ Frame frame = new Frame( "FlatAWTTest" );
+ frame.addWindowListener( new WindowAdapter() {
+ @Override
+ public void windowClosing( WindowEvent e ) {
+ System.exit( 0 );
+ }
+ } );
+ frame.setLayout( new FlowLayout() );
+
+ frame.add( new Label( "text" ) );
+ frame.add( new Button( "text" ) );
+ frame.add( new Checkbox( "text" ) );
+
+ CheckboxGroup checkboxGroup = new CheckboxGroup();
+ frame.add( new Checkbox( "radio 1", true, checkboxGroup ) );
+ frame.add( new Checkbox( "radio 2", false, checkboxGroup ) );
+ frame.add( new Checkbox( "radio 3", false, checkboxGroup ) );
+
+ Choice choice = new Choice();
+ choice.add( "item 1" );
+ choice.add( "item 2" );
+ choice.add( "item 3" );
+ frame.add( choice );
+
+ frame.add( new TextField( "text" ) );
+ frame.add( new TextArea( "text" ) );
+
+ List list = new List();
+ list.add( "item 1" );
+ list.add( "item 2" );
+ frame.add( list );
+
+ frame.add( new Scrollbar() );
+ frame.add( new ScrollPane() );
+ frame.add( new Panel() );
+ frame.add( new Canvas() );
+
+ frame.setSize( 800, 600 );
+ frame.setVisible( true );
+ });
+ }
+}
diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatStressTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatStressTest.java
new file mode 100644
index 00000000..58b39a48
--- /dev/null
+++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatStressTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2021 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.Container;
+import java.awt.FlowLayout;
+import java.util.Random;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.SwingUtilities;
+import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.FlatLightLaf;
+
+/**
+ * @author Karl Tauber
+ */
+public class FlatStressTest
+{
+ public static void main( String[] args ) {
+ SwingUtilities.invokeLater( () -> {
+ FlatLightLaf.setup();
+ new FlatStressTest();
+ } );
+ }
+
+ protected FlatStressTest() {
+ JFrame frame = new JFrame( "FlatStressTest" );
+ frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
+
+ Container contentPane = frame.getContentPane();
+ contentPane.setLayout( new FlowLayout() );
+
+ contentPane.add( createStressTest() );
+
+ frame.setSize( 800, 600 );
+ frame.setLocationRelativeTo( null );
+ frame.setVisible( true );
+ }
+
+ private JComponent createStressTest() {
+ return createComboBoxStressTest();
+ }
+
+ // for https://github.com/JFormDesigner/FlatLaf/issues/432
+ // simulates StackOverflowError in FlatComboBoxUI when doing stuff in various threads
+ //
+ // requires adding `Thread.sleep( 1 );` to `FlatComboBoxUI.CellPaddingBorder.install()`
+ // after invocation of `uninstall()`
+ private JComponent createComboBoxStressTest() {
+ Random random = new Random();
+
+ JComboBox