ADDITIONAL SYSTEM INFORMATION :
Microsoft Windows [Version 10.0.17134.1040]
openjdk version "13" 2019-09-17
OpenJDK Runtime Environment AdoptOpenJDK (build 13+33)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 13+33, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
Menu bar focused item selection remains after window has lost focus due to lack of inbound references to the listener which is held inside WeakChangeListener.
As you may see in below code, a ChangeListener passed to constructor is not held anywhere inside MenuBarSkin, so it can be and will be eventually GCed.
weakWindowFocusListener = new WeakChangeListener<Boolean>((ov, t, t1) -> {
if (!t1) {
unSelectMenus();
}
});
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Run provided executable test case.
2. Press "Add MenuBar" button.
3. Having focused the window press and release Alt button.
4. Lose focus of application's window - click on the desktop.
5. Focus back the window.
6. Press "Run GC" button.
7. Repeat step 3-4.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
After 2. Menu bar is added on top of the window.
After 3. First menu item is selected/focused.
After 4. Menu selection is removed.
After 7. Menu selection is removed.
ACTUAL -
After 2. Menu bar is added on top of the window.
After 3. First menu item is selected/focused.
After 4. Menu selection is removed.
After 7. Menu selection remains visible.
---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MenuBarWeakRefIssue
{
public static void main( String[] args )
{
System.err.println( Runtime.version().toString() );
Application.launch( MenuBarWeakRefIssue.MainFx.class, args );
}
public static class MainFx extends Application
{
private boolean firstTime = true;
@Override
public void start( final Stage primaryStage ) throws Exception
{
final var runGc = new Button( "Run GC" );
runGc.setOnAction( e -> {
System.gc();
} );
final var addMenuBar = new Button( "Add MenuBar" );
final BorderPane borderPane = new BorderPane( new VBox( runGc, addMenuBar ) );
addMenuBar.setOnAction( e -> {
if( firstTime )
{
recreateMenuBar( borderPane );
}
recreateMenuBar( borderPane );
} );
Scene scene = new Scene( borderPane, 800, 600 );
primaryStage.setScene( scene );
primaryStage.show();
}
private void recreateMenuBar( final BorderPane aBorderPane )
{
final var menuBar = new MenuBar( new Menu( "_One" ), new Menu( "_Two" ), new Menu( "T_hree" ) );
aBorderPane.setTop( menuBar );
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
There is no workaround, but there is a solution:
weakWindowFocusListener = new WeakChangeListener<Boolean>((ov, t, t1) -> {
if (!t1) {
unSelectMenus();
}
});
change above to:
weakWindowFocusListener = new WeakChangeListener<Boolean>( windowFocusListener = (ov, t, t1) -> {
if (!t1) {
unSelectMenus();
}
});
and add reference to "windowFocusListener".
FREQUENCY : always
Microsoft Windows [Version 10.0.17134.1040]
openjdk version "13" 2019-09-17
OpenJDK Runtime Environment AdoptOpenJDK (build 13+33)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 13+33, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
Menu bar focused item selection remains after window has lost focus due to lack of inbound references to the listener which is held inside WeakChangeListener.
As you may see in below code, a ChangeListener passed to constructor is not held anywhere inside MenuBarSkin, so it can be and will be eventually GCed.
weakWindowFocusListener = new WeakChangeListener<Boolean>((ov, t, t1) -> {
if (!t1) {
unSelectMenus();
}
});
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Run provided executable test case.
2. Press "Add MenuBar" button.
3. Having focused the window press and release Alt button.
4. Lose focus of application's window - click on the desktop.
5. Focus back the window.
6. Press "Run GC" button.
7. Repeat step 3-4.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
After 2. Menu bar is added on top of the window.
After 3. First menu item is selected/focused.
After 4. Menu selection is removed.
After 7. Menu selection is removed.
ACTUAL -
After 2. Menu bar is added on top of the window.
After 3. First menu item is selected/focused.
After 4. Menu selection is removed.
After 7. Menu selection remains visible.
---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MenuBarWeakRefIssue
{
public static void main( String[] args )
{
System.err.println( Runtime.version().toString() );
Application.launch( MenuBarWeakRefIssue.MainFx.class, args );
}
public static class MainFx extends Application
{
private boolean firstTime = true;
@Override
public void start( final Stage primaryStage ) throws Exception
{
final var runGc = new Button( "Run GC" );
runGc.setOnAction( e -> {
System.gc();
} );
final var addMenuBar = new Button( "Add MenuBar" );
final BorderPane borderPane = new BorderPane( new VBox( runGc, addMenuBar ) );
addMenuBar.setOnAction( e -> {
if( firstTime )
{
recreateMenuBar( borderPane );
}
recreateMenuBar( borderPane );
} );
Scene scene = new Scene( borderPane, 800, 600 );
primaryStage.setScene( scene );
primaryStage.show();
}
private void recreateMenuBar( final BorderPane aBorderPane )
{
final var menuBar = new MenuBar( new Menu( "_One" ), new Menu( "_Two" ), new Menu( "T_hree" ) );
aBorderPane.setTop( menuBar );
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
There is no workaround, but there is a solution:
weakWindowFocusListener = new WeakChangeListener<Boolean>((ov, t, t1) -> {
if (!t1) {
unSelectMenus();
}
});
change above to:
weakWindowFocusListener = new WeakChangeListener<Boolean>( windowFocusListener = (ov, t, t1) -> {
if (!t1) {
unSelectMenus();
}
});
and add reference to "windowFocusListener".
FREQUENCY : always