I have created CustomMenuBar
class that extends javafx.scene.control.MenuBar
, and the thing I want to implement is to add new menus only by String
value like shown in start
method of following code:
package recursivemenu;
import javafx.application.Application;
import javafx.collections.ObservableList;
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 RecursivelyAddMenuAndSubMenu extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
String menu1 = "File > Open";
String menu2 = "File > Close";
String menu3 = "File > Recently closed > File1";
String menu4 = "File > Recently closed > File2";
String menu5 = "File > Recently closed > File3";
String menu6 = "File > Recently closed > File4";
String menu7 = "File > Recently closed > File5";
CustomMenuBar customMenuBar = new CustomMenuBar();
customMenuBar.addMenu(menu1);
customMenuBar.addMenu(menu2);
customMenuBar.addMenu(menu3);
customMenuBar.addMenu(menu4);
customMenuBar.addMenu(menu5);
customMenuBar.addMenu(menu6);
customMenuBar.addMenu(menu7);
BorderPane borderPane = new BorderPane();
borderPane.setTop(customMenuBar);
Scene scene = new Scene(borderPane);
primaryStage.setScene(scene);
primaryStage.setMaximized(true);
primaryStage.show();
}
class CustomMenuBar extends MenuBar {
void addMenu(String menu) {
String[] menuChain = splitMenus(menu);
// Add new top menu if not exists.
if (getMenu(menuChain[0], getMenus()) == null)
createMenu(menuChain[0]);
// Adding sub menus
Menu topMenu = getMenu(menuChain[0], getMenus());
if (topMenu.getItems().isEmpty()) {
addSubMenu(topMenu, menuChain[1]);
} else {
// Add sub menu if not exists.
if (getItem(menuChain[1], topMenu.getItems()) == null)
createSubMenu(menuChain[1], topMenu);
}
}
private void createSubMenu(String subMenuText, Menu menu) {
menu.getItems().add(new MenuItem(subMenuText));
}
private MenuItem getItem(String subMenuText, ObservableList<MenuItem> items) {
for (MenuItem item : items) {
if (item.getText().equals(subMenuText)) {
return item;
}
}
return null;
}
private void addSubMenu(Menu topMenu, String subMenuText) {
topMenu.getItems().add(new MenuItem(subMenuText));
}
private void createMenu(String menuText) {
getMenus().add(new Menu(menuText));
}
private Menu getMenu(String menuText, ObservableList<Menu> menus) {
for (Menu menu : menus) {
if (menu.getText().equals(menuText))
return menu;
}
return null;
}
private String[] splitMenus(String menuText) {
String[] menuChain = menuText.split("\\s*>\\s*");
for (int i = 0; i < menuChain.length; i++)
menuChain[i] = menuChain[i].trim();
return menuChain;
}
}
}
String format should be like Menu > SubMenu > SubMenu > etc.
And this is example I am only capable to add menu with one sub menu. And I am stuck in here, and don't know to make addMenu
method add all menus and sub menus recursively.
The end result should look like this:
Edit:
Menus and sub menus should not be duplicated and they should have to keep sequence of hierarchy.
Edit2:
CustomMenuBar should be able to add any length of nested sub menus.
e.g.
It should work with Menu > SubMenu1 > SubMenu2 > SubMenu3
It should work with Menu > SubMenu1 > SubMenu2 > SubMenu3 > SubMenu4
too.
and with Menu > SubMenu1 > SubMenu2 > SubMenu3 > SubMenu4 > ... > ...> etc as well.
control. Menu class provides all the methods to deal with menus. This class needs to be instantiated to create a Menu. The following sample of code shows the implementation of JavaFX menu.
A stage has two parameters determining its position namely Width and Height. It is divided as Content Area and Decorations (Title Bar and Borders).
You can create a Button by instantiating the javafx. scene. control. Button class of this package and, you can set text to the button using the setText() method.
I tried to keep it as simple as I could. I will leave the optimization to you. You can find comments inside the code for more info about the entire process and logic.
import javafx.application.Application;
import javafx.collections.ObservableList;
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 RecursivelyAddMenuAndSubMenu extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
String menu1 = "File > Open";
String menu2 = "File > Close";
String menu3 = "File > Recently closed > File1";
String menu4 = "File > Recently closed > File2";
String menu5 = "File > Recently closed > File3";
String menu6 = "File > Recently closed > File4";
String menu7 = "File > Recently closed > File5";
String menu8 = "File > Recently closed > File5 > something";
CustomMenuBar customMenuBar = new CustomMenuBar();
customMenuBar.addMenu(menu1);
customMenuBar.addMenu(menu2);
customMenuBar.addMenu(menu3);
customMenuBar.addMenu(menu4);
customMenuBar.addMenu(menu5);
customMenuBar.addMenu(menu6);
customMenuBar.addMenu(menu7);
customMenuBar.addMenu(menu8);
BorderPane borderPane = new BorderPane();
borderPane.setTop(customMenuBar);
Scene scene = new Scene(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
class CustomMenuBar extends MenuBar {
private Menu currentMenu;
private Menu head;
void addMenu(String menu) {
String tokens[] = splitMenusInHalf(menu);
// we found something like "x -> y ... "
if (tokens.length > 1) {
// search for the current root if it contains
// the menu we are about to create
currentMenu = this.getMenu(tokens[0], head);
boolean isAdded = true;
// if not create it
if (currentMenu == null) {
currentMenu = new Menu(tokens[0]);
isAdded = false;
}
// find out if there was a previous Menu created
// if so the current is a sub-menu of the previous one
if (head == null) {
head = currentMenu;
if (!isAdded) {
this.getMenus().add(currentMenu);
}
} else {
if (!isAdded) {
// otherwise add the current Menu as sub-menu
head.getItems().add(currentMenu);
}
// set the Current "head" the sub-menu
head = currentMenu;
}
// Recursive check for more menus or menuItems
addMenu(tokens[1]);
} else {
// If found only something like "x" which is MenuItem
currentMenu.getItems().add(new MenuItem(tokens[0]));
// reset everything for the next addMenu call
currentMenu = null;
head = null;
}
}
private Menu getMenu(String menuText, Menu root) {
if (root == null) {
ObservableList<Menu> allMenus = this.getMenus();
for (Menu m : allMenus) {
if (m.getText().equals(menuText)) {
return m;
}
}
} else {
ObservableList<MenuItem> allMenus = root.getItems();
for (MenuItem m : allMenus) {
if (m.getText().equals(menuText)) {
// We are about to convert MenuItem to Menu
if (!(m instanceof Menu)) {
// Get the previous menuItem location
int index = allMenus.indexOf(m);
// Remove it
allMenus.remove(m);
// Create a new Menu with the previous MenuItem text
m = new Menu(menuText);
// Add it to the correct location
allMenus.add(index, m);
}
return (Menu) m;
}
}
}
return null;
}
private String[] splitMenusInHalf(String menuText) {
String[] menuChain = menuText.split("\\s*>\\s*", 2);
for (int i = 0; i < menuChain.length; i++)
menuChain[i] = menuChain[i].trim();
return menuChain;
}
}
}
As the OP asked the getMenu() will now convert any previous MenuItem to Menu if the user tried to add a sub-menu or a MenuItem on it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With