From 8b5a738e65279ccb255899f89f3b1d7c8418173a Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Tue, 12 Jul 2022 10:33:53 +0200 Subject: [PATCH] Menus: avoid that `SubMenuUsabilityHelper` can be installed multiple times, which can freeze the application caused pushing multiple event queues and popping wrong event queue first (e.g. NetBeans Form Editor invokes `FlatLaf.initialize()` but not `uninitialize()`) (PR #490; https://github.com/apache/netbeans/issues/4231) --- CHANGELOG.md | 5 ++++ .../java/com/formdev/flatlaf/FlatLaf.java | 11 ++++----- .../flatlaf/SubMenuUsabilityHelper.java | 24 +++++++++++++++---- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0181e076..9f94b41c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ FlatLaf Change Log - ComboBox: Fixed vertical alignment of text in popup list with text in combo box in IntelliJ/Darcula themes. +- Menus: Fixed application freeze under very special conditions (invoking + `FlatLaf.initialize()` twice in NetBeans GUI builder) and using menu that has + submenus. See + [NetBeans issue #4231](https://github.com/apache/netbeans/issues/4231#issuecomment-1179611682) + for details. - MenuItem: Fixed sometimes wrapped HTML text on HiDPI screens on Windows. - TableHeader: Fixed exception when changing table structure (e.g. removing column) from a table header popup menu action. (issue #532) 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 9e287b9d..95f76a30 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java @@ -103,7 +103,7 @@ public abstract class FlatLaf private PopupFactory oldPopupFactory; private MnemonicHandler mnemonicHandler; - private SubMenuUsabilityHelper subMenuUsabilityHelper; + private boolean subMenuUsabilityHelperInstalled; private Consumer postInitialization; private List> uiDefaultsGetters; @@ -246,8 +246,7 @@ public abstract class FlatLaf mnemonicHandler.install(); // install submenu usability helper - subMenuUsabilityHelper = new SubMenuUsabilityHelper(); - subMenuUsabilityHelper.install(); + subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install(); // listen to desktop property changes to update UI if system font or scaling changes if( SystemInfo.isWindows ) { @@ -329,9 +328,9 @@ public abstract class FlatLaf } // uninstall submenu usability helper - if( subMenuUsabilityHelper != null ) { - subMenuUsabilityHelper.uninstall(); - subMenuUsabilityHelper = null; + if( subMenuUsabilityHelperInstalled ) { + SubMenuUsabilityHelper.uninstall(); + subMenuUsabilityHelperInstalled = false; } // restore default link color diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/SubMenuUsabilityHelper.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/SubMenuUsabilityHelper.java index 157e8a94..a6bc0176 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/SubMenuUsabilityHelper.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/SubMenuUsabilityHelper.java @@ -59,6 +59,11 @@ class SubMenuUsabilityHelper private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle"; private static final String KEY_SHOW_SAFE_TRIANGLE = "FlatLaf.debug.menu.showSafeTriangle"; + // Using a static field to ensure that there is only one instance in the system. + // Multiple instances would freeze the application. + // https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607 + private static SubMenuUsabilityHelper instance; + private SubMenuEventQueue subMenuEventQueue; private SafeTrianglePainter safeTrianglePainter; private boolean changePending; @@ -74,13 +79,22 @@ class SubMenuUsabilityHelper private Rectangle invokerBounds; - void install() { - MenuSelectionManager.defaultManager().addChangeListener( this ); + static synchronized boolean install() { + if( instance != null ) + return false; + + instance = new SubMenuUsabilityHelper(); + MenuSelectionManager.defaultManager().addChangeListener( instance ); + return true; } - void uninstall() { - MenuSelectionManager.defaultManager().removeChangeListener( this ); - uninstallEventQueue(); + static synchronized void uninstall() { + if( instance == null ) + return; + + MenuSelectionManager.defaultManager().removeChangeListener( instance ); + instance.uninstallEventQueue(); + instance = null; } @Override