JavaFX MenuItem, handling the event - javafx

I'm developing an small application and I have a problem when creating the menu bar.
This is my start method:
public void start(#SuppressWarnings("exports") Stage prymaryStage) throws Exception {
// Stats menu
Menu statsMenu = new Menu("Stats");
// PairName menu
Menu pairNameMenu = new Menu("Choose pair");
// Stats Menu items
MenuItem gStats = new MenuItem("General stats");
// Pair list
ArrayList<String> pairNameList = DatabaseMethods.returnPairNameList();
// PairName items (probably i will have to change)
for (String item : pairNameList) {
pairNameMenu.getItems().add(new MenuItem(item));
}
statsMenu.getItems().addAll(gStats, pairNameMenu);
// Main menu bar
MenuBar menuBar = new MenuBar();
menuBar.getMenus().addAll(statsMenu);
// BorderPane settings
BorderPane borderPane = new BorderPane();
borderPane.setTop(menuBar);
Scene scene = new Scene(borderPane, 1200, 800);
prymaryStage.setTitle("English minimal pair training");
prymaryStage.setScene(scene);
prymaryStage.show();
}
The problem I have is in this part of the code:
ArrayList<String> pairNameList = DatabaseMethods.returnPairNameList();
// PairName items (probably i will have to change)
for (String item : pairNameList) {
pairNameMenu.getItems().add(new MenuItem(item));
}
I was trying to create the items of a submenu from an ArrayList. This data is fetch from a database and the data is returned in form of an ArrayList. I didn't find any other way to do the menu items than pairNameMenu.getItems().add(new MenuItem(item)); inside the for loop.
Now I want to handle the click in the items but I don't know how to do it. I've tried with .setOnAction but Eclipse says the .add(new MenuItem(item)) can't be use in that case and recomends .addAll and the same happens, Eclipse says that's an error and recomends .add
I tried to add this code after new MenuItem(item)
.addEventHandler(new EventHandler<ActionEvent>() {
public void handle(ActionEvent even) {
}
})
But it didn't work either.
I'm pretty new to Java and JavaFX, this is my first project so sorry if this is a very basic question.
Thank you for your time

You have to loop through pairNameMenu items after you have created and added all the items to pairNameMenu:
pairNameMenu.getItems().foreach((item) ->{
item.addEventHandler.....
....
....
});
or do something like below when creating the MenuItems:
for (String item : pairNameList) {
MenuItem tempMenuItem = new MenuItem(item);
tempMenuItem..addEventHandler.....
....
....
pairNameMenu.getItems().add(tempMenuItem);
}

Related

How to disable the arrow button in JavaFX combo box

I have a project I am working on. I am trying to make a dictionary. For that, I have a .csv file with about 55000 words.I am using the trie data structure which has a startsWith() method which checks whether there is a word in the .csv file which matches the given prefix. I had managed to get it to work to find all words that match the given prefix and display them. Now, I have to develop this into a JavaFX app.
So, I thought of using a ComboBox which has its editable attribute set to true so that I could type into it and then the handler associated with the textProperty() of its editor would display all the words starting with given prefix in the listview of the combobox.
Now, the problem I have is that whenever I click the arrow button of the combobox the application stops responding (I think it's because the list view tries to resize itself to fit the items which are 55000).
So, what I want to know is how to disable the arrow button entirely. I have tried to set its background-color to transparent but even then it can still be clicked I want to make it so that it is disabled and transparent basically the combobox ends up looking like a text field.
If there are better, more efficient ways of implementing a dictionary I would appreciate it if you could guide me.
The ListView is a virtual control that only shows a certain number of cells at a time, it doesn't need to "resize itself to the number of items" in any way that would lock up your GUI.
Does this demo program do what you want?
public class Main extends Application {
public static void main(String[] args) throws IOException, URISyntaxException {
launch(args);
}
#Override
public void start(Stage stage) {
List<String> rawWords = Collections.emptyList();
try {
URI wordURI = new URI("https://www-cs-faculty.stanford.edu/~knuth/sgb-words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(wordURI.toURL().openStream(), StandardCharsets.UTF_8));
rawWords = reader.lines().collect(Collectors.toCollection(() -> new ArrayList<>(6000)));
} catch (IOException | URISyntaxException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
// make the list at least as big as in the question
while(rawWords.size() < 55000) {
ArrayList<String> nextWords = new ArrayList<>(rawWords.size() * 2);
nextWords.addAll(rawWords);
nextWords.addAll(rawWords);
rawWords = nextWords;
}
Collections.sort(rawWords);
ObservableList<String> wordList = FXCollections.observableArrayList(rawWords);
FilteredList<String> filteredList = new FilteredList<>(wordList);
ComboBox<String> combo = new ComboBox<>(filteredList);
combo.setEditable(true);
combo.getEditor().textProperty().addListener((obs, oldVal, newVal) -> {
filteredList.setPredicate(s -> newVal == null || newVal.isEmpty() || s.startsWith(newVal));
});
VBox vbox = new VBox(8,new Label("Dictionary ComboBox"),
combo,
new Label("\n\n\n\nThis space intentionally left blank.\n\n\n\n"));
vbox.setPadding(new Insets(8));
Scene scene = new Scene(vbox, 400, 300);
stage.setTitle("Demo - Filtered Combobox List");
stage.setScene(scene);
stage.show();
}
}

JavaFX keyboard input stops working after adding buttons

I am building a game engine as a school project. When I add button to the same group as where I have my canvas, I can't control my player anymore with keyboard. Buttons and everything else still works like normal.
My code is pretty huge, so this is a simplified code of the problem:
public abstract class GEngine extends Application {
public void start(Stage theStage) throws Exception {
try {
this.stage = theStage;
Group root = new Group();
Scene theScene = new Scene(root);
stage.setScene(theScene);
Canvas canvas = new Canvas(windowWidth, windowHeight);
root.getChildren().add(canvas);
Button btn = new Button("new");
btn.setOnMousePressed(e->System.out.println("press"));
root.getChildren().add(btn);
Timeline gameLoop = new Timeline();
gameLoop.setCycleCount(Timeline.INDEFINITE);
// Handle input
theScene.setOnKeyPressed(Input::handlePressed);
theScene.setOnKeyReleased(Input::handleReleased);
// Control game loop and it's speed
KeyFrame kf = new KeyFrame(
Duration.seconds(0.017), // 60 FPS
(e)->this.controlGameLoop());
gameLoop.getKeyFrames().add( kf );
gameLoop.play();
stage.show();
} catch (Exception e) {
e.printStackTrace();
stop();
}
}
}
There is probably something happening on the background which I just don't understand. I can show my Input class code too if somebody wants to see it, but in my understanding it's not necessary.
I have tried using AnchorPane as main root and make a separate groups for buttons and canvas which I add the to the AnchorPane, but that did not help at all. That was pretty much the only offered solution I could find from google.
Adding btn.setFocusTraversable(false); fixed the problem, thanks to Luxusproblem for providing the answer!

JavaFX Menu (not MenuItem) with ToolTip?

is there any possibility to add a Tooltip to a JavaFX (Sub-)Menu?
The usual (but ugly - why can't a menu not just be made of nodes?!) solution for MenuItems is to use a CustomMenuItem and put a Label (which is a Node) in it - the label can be assigned a ToolTip.
But how can I achieve this for a (Sub-)Menu? See the following example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
public class CustomSubMenu extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
MenuButton menuButton = new MenuButton("Menu");
Label helloLabel = new Label("Hello...");
helloLabel.tooltipProperty().setValue(new Tooltip("World!"));
menuButton.getItems().add(new CustomMenuItem(helloLabel));
Menu submenu = new Menu("This Submenu needs a ToolTip!");
// new CustomMenuItem(new Menu()); // doesn't work, because Menu is not a Node.
submenu.getItems().add(new MenuItem("Some other Item"));
menuButton.getItems().add(submenu);
primaryStage.setScene(new Scene(menuButton));
primaryStage.show();
}
}
Each menuItem (and naturally also a menu) is associated with a node. That node is accessible after the item has been shown at least once. Then the node is accessible via item.getStyleableNode() (since fx9, for fx8 see below) and a tooltip can be set on that node.
So basically, the way to go is to listen for that instant and then install a tooltip. The example below does so by
create a tooltip for the menu/item and put it into its properties
register a onShown handler on the parent menu and install the tooltip if available
A basic snippet:
String tooltipKey = "TOOL_TIP";
MenuItem normalItem = new MenuItem("Good .. ");
normalItem.getProperties().put(tooltipKey, new Tooltip("Morning!"));
menuButton.getItems().add(normalItem);
Menu submenu = new Menu("This Submenu needs a ToolTip!");
submenu.getProperties().put(tooltipKey, new Tooltip("It's meee!"));
menuButton.setOnShown(e -> {
menuButton.getItems().forEach(item -> {
Node node = item.getStyleableNode();
if (node != null && item.getProperties().get(tooltipKey) instanceof Tooltip) {
Tooltip tip = (Tooltip) item.getProperties().get(tooltipKey);
Tooltip.install(node, tip);
}
});
});
For fx8, the basic approach is the same - but access to the node that represents the menuItem is nasty (beware: don't in production! *cough ..):
getStyleableNode is new to fx9, so we have to hack around using internal api and implementation details
the time at which the node is available is harder to find: the obvious hook would be an eventHandler for SHOWN on the menuButton, but that doesn't seem to be supported (doesn't seem to be supported yet - didn't dig though)
one way around is to first listen to the showingProperty, grab the contextMenu, listen to its skinProperty and do the install once the skin is set
code snippets:
// not working - what's wrong?
menuButton.addEventHandler(MenuButton.ON_SHOWN, e -> {
LOG.info("not getting here?");
// install tooltips here
});
ChangeListener<Skin> skinListener = (src, ov, skin) -> {
ContextMenuContent content = (ContextMenuContent) skin.getNode();
VBox menuBox = (VBox) content.getChildrenUnmodifiable().get(0);
menuBox.getChildren().forEach(node -> {
// implementation detail: the menuItem is set in the node's properties
if (node.getProperties().get(MenuItem.class) instanceof MenuItem) {
MenuItem item = (MenuItem) node.getProperties().get(MenuItem.class);
if (node != null && item.getProperties().get(tooltipKey) instanceof Tooltip) {
Tooltip tip = (Tooltip) item.getProperties().get(tooltipKey);
Tooltip.install(node, tip);
}
}
});
};
menuButton.showingProperty().addListener((src, ov, nv) -> {
ContextMenu popup = submenu.getParentPopup();
if (popup != null) {
if (popup.getSkin() == null) {
popup.skinProperty().addListener(skinListener);
} else {
popup.skinProperty().removeListener(skinListener);
}
}
});
I figured out a simple way to trick the MenuItem as a Control Label node, use code like below:
Label lb=new Label("MenuItem Text");
lb.setStyle("-fx-text-fill:black;");
MenuItem myMenuItem = new MenuItem(null, lb);
Tooltip tips = new Tooltip("Your tip text here");
Tooltip.install(myMenuItem.getGraphic(), tips);
set the tooltip to the label and this works pretty good for me.

JAVAFX Tree view with different context menu

I want to create context menu for treeitems in a treeview. The thing is I want to display different context menu for each treeItem. How to implement this?
Foe example I want to create "Add Employee" for Acc Dept and "Add Supporter" for IT support.
Based on name of the treeitem the context menu needs to be displayed.
public TreeModel() {
MenuItem addMenuItem = new MenuItem("Create Tab");
addMenu.getItems().add(addMenuItem);
addMenuItem.setOnAction(new EventHandler() {
#Override
public void handle(Event t) {
TreeItem newEmployee =
new TreeItem<>("New Tab");
getTreeItem().getChildren().add(newEmployee);
}
});
contextMenuProperty().bind(
Bindings.when(Bindings.equal(itemProperty(),"TABS"))
.then(addMenu)
.otherwise((ContextMenu)null));
}
This works. #James thanks a lot for your excellent article :)

How an action can be performed when clicking on a menu item?

I am doing a project in javafx using Netbeans IDE. I am new to javafx. I have a menu bar in my project. I need to open a new page on the same windows when clicked on each menu item(not new scene). The code is given below :
private VBox addVBox1() {
final VBox vbox = new VBox();
vbox.setPadding(new Insets(20,40,30,4));
vbox.setSpacing(10);
MenuBar menuBar = new MenuBar();
Menu menuFile1 = new Menu("ADD");
Menu menuFile2 = new Menu("EDIT");
Menu menuFile3 = new Menu("VIEW");
Menu menuFile4 = new Menu("HELP");
MenuItem add1 = new MenuItem("ENTER STUDENT DETAILS");
MenuItem add2 = new MenuItem("ENTER C-MARK");
MenuItem add3 = new MenuItem("ENTER ATTENDANCE");
MenuItem add4 = new MenuItem("EDIT STUDENT DETAILS");
MenuItem add6 = new MenuItem("EDIT C-MARK");
MenuItem add8 = new MenuItem("EDIT ATTENDANCE");
MenuItem add10 = new MenuItem("STUDENT DETAILS");
MenuItem add11 = new MenuItem("C-MARK");
MenuItem add12 = new MenuItem("ATTENDANCE");
MenuItem add13 = new MenuItem("VIEW HELP");
add1.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
//...WHAT TO INCLUDE HERE ?
}
});
menuFile1.getItems().addAll(add1,add2,add3);
menuFile2.getItems().addAll(add4,add6,add8);
menuFile3.getItems().addAll(add10,add11,add12);
menuFile4.getItems().addAll(add13);
menuBar.getMenus().addAll(menuFile1,menuFile2,menuFile3,menuFile4);
vbox.getChildren().addAll(menuBar);
return vbox;
}
In my project, I open new pages when clicking on buttons. Its code is:
btn2.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
((Stage)btn2.getScene().getWindow()).setScene(new Scene(new Login()));
}
});
Is it possible to implement this code in case of menu item ? How should I edit this code to perform an action when clicked on a menu item ?
I don't know exactly what you mean by a new page. There is a Pagination control, but I don't think you mean that. Here's how to add a TextArea but you have to design the UI and choose your own controls.
add1.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
vbox.getChildren().add(new TextArea());
}
});
This is how to make a new window but you may want to ask a question about designing dialog boxes. Here's a SO answer https://stackoverflow.com/a/14168238/2855515
add1.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
Stage stage = new Stage();
Scene scene = new Scene(new VBox());
stage.setTitle("popup");
stage.setScene(scene);
stage.show();
}
});
I think this question has not been answered correctly and the question has been misinterpreted.
TomJ asked:
Is it possible to implement this code in case of menu item?
When he says "menu item", Does he mean Menu or MenuItem? He's clearing showing the correct code for how to do it for a MenuItem. I think he's asking about how to do that same thing for a Menu. I tried doing this for a Menu, and the code for invoking a handler on a Menu is slightly different than doing it for a MenuItem.
Here's a simple code example where the execute method is called when the Execute Menu is clicked:
Menu viewMenu = new Menu("View");
MenuItem viewJobs = new MenuItem("Jobs");
viewJobs.setOnAction(viewJobsAction);
MenuItem viewFileSelection = new MenuItem("File Selection");
viewFileSelection.setOnAction(e->{viewFileSelection());
viewMenu.getItems().addAll(viewJobs, viewFileSelection);
Menu execute = new Menu("Execute");
execute.onShownProperty().setValue(e->{execute());
// if the following line is not added, the onShownProperty event handler
// will never be called!
execute.getItems().addAll(new MenuItem());
Menu help = new Menu("Help");
MenuItem helpItem = new MenuItem("Help");
helpItem.setOnAction(e->{showHelp()}
MenuItem aboutItem = new MenuItem("About");
aboutItem.setOnAction(e->{showAbout()}
help.getItems().addAll(helpItem,aboutItem);
menubar.getMenus().addAll(viewMenu, execute, help);
It's necessary to have a bogus MenuItem added to the Execute Menu for this to work. For Menu, it's better to use the onShownProperty or onShowingProperty rather than using the setOnAction. Any of them will work, but the setOnAction will require a second click before the handler will be called.
The JavaDoc for onShowingProperty for Menu says:
Called just prior to the ContextMenu being shown, even if the menu has
no items to show. Note however that this won't be called if the menu
does not have a valid anchor node.
It sounds like the code should work without any MenuItems added, but it doesn't. It says however that the call won't be made if the menu doesn't have a valid anchor node. I'm not sure what an anchor node is, and I couldn't find any documentation about how to add an anchor node to a menu. Adding the bogus empty MenuItem clearly made the Execute Menu have a valid anchor node. If someone knows more about anchor nodes, please reply and explain it, but the code example I gave works acceptably.

Resources