A DESCRIPTION OF THE PROBLEM :
Adding an additional menubar or removing a menu from a non-system menubar causes the current system menubar to disappear.
This problem looks like a regression introduced by the fix forJDK-8087709.
On line 869 in MenuBarSkin we seem to assume that the menubar that we are acting on is always the system menubar. If that is not the case the current system menubar is removed anyway (by calling setUseSystemMenuBar(false) on line 871).
This code is in the sceneProperty listener created in the MenuBarSkin.rebuildUI() method, where it is also called explicitly on line 903. This means that any scene change for a menubar or any change that causes rebuildUI() to be called (such as removing a menu from a menubar) will cause the current system menubar to disappear.
The problem can be fixed by adding the following test to the if clause starting on line 869 in MenuBarSkin:
getSkinnable().isUseSystemMenuBar()
I intend to create a GitHub pull request with this fix and a corresponding unit test.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Create a MenuBar and call setUseSystemMenuBar(true) on it
- Add the menubar to a Scene and show the scene in a Stage
- Create another MenuBar and add it to the scene
The provided executable test case does the above.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The first MenuBar is still used for the system menubar.
If the provided executable test case is run both log statements should print:
INFO: isUseSystemMenuBar = true
ACTUAL -
The first MenuBar is no longer used in the system menubar and its isUseSystemMenuBar property has been set to false.
If the provided executable test case is run the second log statement prints:
INFO: isUseSystemMenuBar = false
---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class MenubarTest extends Application {
private static final System.Logger logger = System.getLogger(MenubarTest.class.getName());
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
MenuBar primaryMenuBar =
new MenuBar(new Menu("Primary Menu", null, new MenuItem("Test")));
primaryMenuBar.setUseSystemMenuBar(true);
logIsUseSystemMenuBar(primaryMenuBar);
BorderPane root = new BorderPane();
root.setTop(primaryMenuBar);
primaryStage.setScene(new Scene(root));
primaryStage.show();
MenuBar secondaryMenuBar = new MenuBar(
new Menu("Secondary Menu", null, new MenuItem("Test")));
root.setBottom(secondaryMenuBar);
Platform.runLater(() -> logIsUseSystemMenuBar(primaryMenuBar));
}
private void logIsUseSystemMenuBar(MenuBar menuBar) {
logger.log(System.Logger.Level.INFO, "isUseSystemMenuBar = {0}", menuBar.isUseSystemMenuBar());
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Make sure to not add or modify any additional menubars after using setUseSystemMenuBar(true).
Adding an additional menubar or removing a menu from a non-system menubar causes the current system menubar to disappear.
This problem looks like a regression introduced by the fix for
On line 869 in MenuBarSkin we seem to assume that the menubar that we are acting on is always the system menubar. If that is not the case the current system menubar is removed anyway (by calling setUseSystemMenuBar(false) on line 871).
This code is in the sceneProperty listener created in the MenuBarSkin.rebuildUI() method, where it is also called explicitly on line 903. This means that any scene change for a menubar or any change that causes rebuildUI() to be called (such as removing a menu from a menubar) will cause the current system menubar to disappear.
The problem can be fixed by adding the following test to the if clause starting on line 869 in MenuBarSkin:
getSkinnable().isUseSystemMenuBar()
I intend to create a GitHub pull request with this fix and a corresponding unit test.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Create a MenuBar and call setUseSystemMenuBar(true) on it
- Add the menubar to a Scene and show the scene in a Stage
- Create another MenuBar and add it to the scene
The provided executable test case does the above.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The first MenuBar is still used for the system menubar.
If the provided executable test case is run both log statements should print:
INFO: isUseSystemMenuBar = true
ACTUAL -
The first MenuBar is no longer used in the system menubar and its isUseSystemMenuBar property has been set to false.
If the provided executable test case is run the second log statement prints:
INFO: isUseSystemMenuBar = false
---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class MenubarTest extends Application {
private static final System.Logger logger = System.getLogger(MenubarTest.class.getName());
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
MenuBar primaryMenuBar =
new MenuBar(new Menu("Primary Menu", null, new MenuItem("Test")));
primaryMenuBar.setUseSystemMenuBar(true);
logIsUseSystemMenuBar(primaryMenuBar);
BorderPane root = new BorderPane();
root.setTop(primaryMenuBar);
primaryStage.setScene(new Scene(root));
primaryStage.show();
MenuBar secondaryMenuBar = new MenuBar(
new Menu("Secondary Menu", null, new MenuItem("Test")));
root.setBottom(secondaryMenuBar);
Platform.runLater(() -> logIsUseSystemMenuBar(primaryMenuBar));
}
private void logIsUseSystemMenuBar(MenuBar menuBar) {
logger.log(System.Logger.Level.INFO, "isUseSystemMenuBar = {0}", menuBar.isUseSystemMenuBar());
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Make sure to not add or modify any additional menubars after using setUseSystemMenuBar(true).