I am trying to make a program that allows a user to select RGB colors through radio buttons, and changing the value from 0 - 255 using a slider. The color changes should apply to a text. When R, G or B is selected, only the selected color should be present in the text (i.e if green is selected, red and blue values are 0).
At the moment the program works to some extent. For example, if the slider is located at value 150, and I selected a new color and then move the slider, the text color is set to 150, or in any case the value the slider is set on before trying to move it to a new value. I have to select a new color before moving the slider if I want it to update. It only updates once for each selected color. I want it to update the selected color seamlessly. Code example below:
public class Oblig5 extends Application {
static int colorValue = 0;
static int red = 0;
static int green = 0;
static int blue = 0;
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
// Create panes
BorderPane bPane = new BorderPane();
VBox vBox = new VBox();
bPane.setLeft(vBox);
// Create text and place it in the pane
Text text = new Text("Oblig 5");
text.setFont(Font.font("Times New Roman", FontWeight.NORMAL, FontPosture.REGULAR, 40));
bPane.setCenter(text);
// Create radio buttons and place them in the VBox
RadioButton rbRed = new RadioButton("Red");
RadioButton rbGreen = new RadioButton("Green");
RadioButton rbBlue = new RadioButton("Blue");
ToggleGroup group = new ToggleGroup();
rbRed.setToggleGroup(group);
rbGreen.setToggleGroup(group);
rbBlue.setToggleGroup(group);
// Create handlers for radiobuttons
rbRed.setOnAction(e -> {
if (rbRed.isSelected()) {
red = colorValue;
green = 0;
blue = 0;
}
});
rbGreen.setOnAction(e -> {
if (rbGreen.isSelected()) {
red = 0;
green = colorValue;
blue = 0;
}
});
rbBlue.setOnAction(e -> {
if (rbBlue.isSelected()) {
red = 0;
green = 0;
blue = colorValue;
}
});
vBox.getChildren().addAll(rbRed, rbGreen, rbBlue);
// Create a slider and place it in the BorderPane
Slider slider = new Slider(0, 255, 135);
slider.setShowTickLabels(true);
slider.setShowTickMarks(true);
bPane.setBottom(slider);
bPane.setAlignment(slider, Pos.CENTER);
// Create a handler for the slider
slider.valueProperty().addListener(ov -> {
colorValue = (int) slider.getValue();
text.setFill(Color.rgb(red, green, blue));
});
// Create a scene and place it in the stage
Scene scene = new Scene(bPane, 400, 400);
primaryStage.setScene(scene);
primaryStage.setTitle("Oblig 5");
primaryStage.show();
}
}
Any inputs are highly appreciated!
[Note: #fabian posted an answer while I was writing this one. Either answer will work, I thought I would post this too since it shows slightly different techniques. As usual in JavaFX there are multiple good ways of achieving the same thing.]
When either the selected button changes, or the slider value changes, you need to set the text fill with the appropriate values. Right now, when the selected button changes, you only update the variables red, green, and blue (but don't change the text fill) and when the slider value changes, you call setFill(...) with red, green, and blue, but don't change the values of those variables.
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Slider;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Oblig5 extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
// Create panes
BorderPane bPane = new BorderPane();
VBox vBox = new VBox();
bPane.setLeft(vBox);
Text text = new Text("Oblig 5");
text.setFont(Font.font("Times New Roman", FontWeight.NORMAL, FontPosture.REGULAR, 40));
bPane.setCenter(text);
RadioButton rbRed = new RadioButton("Red");
RadioButton rbGreen = new RadioButton("Green");
RadioButton rbBlue = new RadioButton("Blue");
ToggleGroup group = new ToggleGroup();
rbRed.setToggleGroup(group);
rbGreen.setToggleGroup(group);
rbBlue.setToggleGroup(group);
vBox.getChildren().addAll(rbRed, rbGreen, rbBlue);
Slider slider = new Slider(0, 255, 135);
slider.setShowTickLabels(true);
slider.setShowTickMarks(true);
bPane.setBottom(slider);
BorderPane.setAlignment(slider, Pos.CENTER);
// Create a handler for the updating text fill:
ChangeListener<Object> updateListener = (obs, oldValue, newValue) -> {
int colorValue = (int) slider.getValue() ;
int red = rbRed.isSelected() ? colorValue : 0 ;
int green = rbGreen.isSelected() ? colorValue : 0 ;
int blue = rbBlue.isSelected() ? colorValue : 0 ;
text.setFill(Color.rgb(red, green, blue));
};
slider.valueProperty().addListener(updateListener);
group.selectedToggleProperty().addListener(updateListener);
// Create a scene and place it in the stage
Scene scene = new Scene(bPane, 400, 400);
primaryStage.setScene(scene);
primaryStage.setTitle("Oblig 5");
primaryStage.show();
}
}
You could set the Color to use as userData to the toggles. This allows you to create a binding for the fill property of the text based on the slider value and the selected toggle:
// create radio buttons and add color at full brightness
RadioButton rbRed = new RadioButton("Red");
rbRed.setUserData(Color.RED);
RadioButton rbGreen = new RadioButton("Green");
rbGreen.setUserData(Color.LIME);
RadioButton rbBlue = new RadioButton("Blue");
rbBlue.setUserData(Color.BLUE);
ToggleGroup group = new ToggleGroup();
rbRed.setToggleGroup(group);
rbGreen.setToggleGroup(group);
rbBlue.setToggleGroup(group);
group.selectToggle(rbRed);
...
// create binding based on toggle user data and slider value
text.fillProperty().bind(Bindings.createObjectBinding(() -> {
Color color = (Color) group.getSelectedToggle().getUserData();
// use color determined by toggle with brightness adjusted based on slider value
return color.deriveColor(0, 1, slider.getValue() / slider.getMax(), 1);
}, slider.valueProperty(), group.selectedToggleProperty()));
Note that using these code snippets there is no need to keep the listeners. Especially the listener updating the text fill should be removes, since the attempt to assign a bound property results in an exception.
Related
I am creating a project for my new understanding of JavaFX GUI. I am just having trouble getting the file to write "Small Cake", "Medium Cake", or "Large Cake" depending on which radio button has been selected. I know most of my logic is working and it has come down to the
writer.write(cakeSize.getSelectedToggle().selectedProperty().getValue().toString());
No matter the documentation I look at or what . selector I choose I can only seem to access the Boolean stating if is it 'true' for selected or not and if not that then it returns the name value as 'selected'
It does not need to use setText(value) I can get rid of those setters if needed I was just trying to find where I went wrong. Without those it just returns in an object the "Small" "Medium" or "Large" based on selection. Those being written on their own to the file I am fine with as well.
Thanks!
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.io.*;
public class BakeryApplication extends Application {
#Override
public void start(Stage stage) {
//Create a pane
//the pane is the layout container that contains other JavaFX components
GridPane gridPane = new GridPane();
//Create a label and pass it through the pane layout
Label orderTitle = new Label("Place your Cake Order Below");
Label nameTitle = new Label("First and Last Name");
Label numberTitle = new Label("Please enter your phone number:");
//Create a text field area
TextField name = new TextField();
TextField number = new TextField();
Label cakeSizeTtl = new Label("Please select your Cake Size:");
RadioButton cakeSm = new RadioButton("Small");
cakeSm.setText("Small Cake");
RadioButton cakeMd = new RadioButton("Medium");
cakeMd.setText("Medium Cake");
RadioButton cakeLg = new RadioButton("Large");
cakeLg.setText("Large Cake");
ToggleGroup cakeSize = new ToggleGroup();
cakeSm.setToggleGroup(cakeSize);
cakeMd.setToggleGroup(cakeSize);
cakeLg.setToggleGroup(cakeSize);
Label cakeTypeTtl = new Label("Please select your Cake Type:");
//Combo Box
ComboBox<String> cakeSelection = new ComboBox<>();
cakeSelection.getItems().addAll("Apple","Carrot", "Cheesecake","Chocolate", "Coffee", "Opera", "Tiramisu");
cakeSelection.setValue("Cake Type");
//create a save and quit button
Button saveBtn = new Button("Save");
Button quitBtn = new Button("Quit");
//Events for buttons
saveBtn.setOnAction(e -> {
try {
BufferedWriter writer = new BufferedWriter( new FileWriter("Order.txt"));
writer.write(number.getText());
writer.newLine();
writer.write(name.getText());
writer.newLine();
//add cakeType selection
// writer.write(cakeSize.getSelectedToggle().selectedProperty().toString());
writer.write(cakeSize.getSelectedToggle().selectedProperty().getValue().toString());
writer.newLine();
writer.write(cakeSelection.getValue());
//add cakeSize selection
writer.close();
} catch (IOException err) {
err.printStackTrace();
}
});
//handles click event on quit button to exit program
quitBtn.setOnAction(e ->{
Platform.exit();
});
//add an HBox to hold the buttons and arrange them horizontally
HBox buttonBox = new HBox(10, saveBtn, quitBtn);
gridPane.setHgap(10);
gridPane.setVgap(10);
//node, column, row
gridPane.setConstraints(orderTitle,2,0);
gridPane.setConstraints(nameTitle,2,1);
gridPane.setConstraints(name, 2, 2);
gridPane.setConstraints(numberTitle,2,3);
gridPane.setConstraints(number, 2, 4);
gridPane.setConstraints(cakeSizeTtl, 3, 5);
gridPane.setConstraints(cakeSm, 3, 6);
gridPane.setConstraints(cakeMd, 3, 7);
gridPane.setConstraints(cakeLg, 3, 8);
gridPane.setConstraints(cakeTypeTtl, 2, 5);
gridPane.setConstraints(cakeSelection, 2, 6);
gridPane.setConstraints(buttonBox, 3, 11);
gridPane.setPadding(new Insets(20));
//use getChildren and add method to place the label node in the pane layout
gridPane.getChildren().addAll(buttonBox, orderTitle, name, number, nameTitle, numberTitle, cakeSm, cakeMd, cakeLg, cakeSizeTtl, cakeSelection, cakeTypeTtl);
//Use BorderPane to aid in layout
//controls are typically inserted into a different
//type of layout and then added into the BorderPane accordingly
//like how our buttons and title are in a GridPane right now
BorderPane mainPain = new BorderPane();
mainPain.setCenter(gridPane);
//add the pane to the scene
Scene scene = new Scene(mainPain, 500, 500);
stage.setTitle("Assignment2 Order Your Cake!");
//Placing the Scene in the stage
stage.setScene(scene);
//Displays the Stage
stage.show();
}
public static void main(String[] args) {
//launches the Stage
launch();
}
}
You can ask the ToggleGroup for the currently selected Toggle using the getSelectedToggle() or selectedToggleProperty() method, as you are already doing.
However Toggle is an interface that doesn't define a text property. The Toggle interface has many implementations, one of which is RadioButton.
Now, since you know that your ToggleGroup only contains RadioButton toggles you can safely cast it and ask for its text.
Briefly:
RadioButton selectedToggle = (RadioButton) cakeSize.getSelectedToggle();
if (selectedToggle != null) // it can be null if nothing is selected
String selectedText = selectedToggle.getText();
I have to use a Radio Button to make three Radio Buttons Red, Blue, and Green. The radio buttons are not changing to those colors. In addition to those changes, the others must stay black font. I commented out the Red setTextFill Font and there was no effect. I also commented the Black fonts and they did not do make an effect. The isSelection works.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ButtonRadio extends Application {
#Override
public void start(Stage primaryStage) {
// primary stage
primaryStage.setTitle("javaFX");
// label for text
Label labelfirst = new Label("Choose a button");
// vBox for buttons
VBox layout = new VBox(3);
// radio buttons
RadioButton radio1, radio2, radio3;
radio1 = new RadioButton("Red");
radio2 = new RadioButton("Blue");
radio3 = new RadioButton("Green");
// ToggleGroup for entering
ToggleGroup group = new ToggleGroup();
// radio button variables of toggle groups
radio1.setToggleGroup(group);
radio2.setToggleGroup(group);
radio3.setToggleGroup(group);
// if statements for radio buttons and fonts red, blue, green
if (group.getSelectedToggle() != null) {
if (radio1.isSelected()) {
radio1.setTextFill(Color.RED);
radio2.setTextFill(Color.BLACK);
radio3.setTextFill(Color.BLACK);
} else if (radio2.isSelected()) {
radio2.setTextFill(Color.BLUE);
radio1.setTextFill(Color.BLACK);
radio3.setTextFill(Color.BLACK);
}
else if (radio3.isSelected())
{
radio3.setTextFill(Color.GREEN);
radio1.setTextFill(Color.BLACK);
radio2.setTextFill(Color.BLACK);
}
}
// layout to put in parent
layout.getChildren().addAll(labelfirst, radio1, radio2, radio3);
// put in scene and stage to show
Scene scene1 = new Scene(layout, 400, 250);
primaryStage.setScene(scene1);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Edit:
I tried adding a listener. It still does not work.
if (radio1.isSelected()) {
radio1.setOnAction((event) -> {
radio1.setTextFill(Color.RED);
});
Your if statement will only run once, checking the selected status of your RadioButtons only when the application first runs.
You need to listen for changes to the selected RadioButton and act accordinly. This is easily done by adding a listener to the ToggleGroup's selectedToggleProperty().
Remove your if block and replace it with something like this:
group.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
// Set the previously-selected RadioButton text to BLACK
if (oldValue != null) ((RadioButton) oldValue).setTextFill(Color.BLACK);
// Set the color for the newly-selected RadioButton
if (newValue.equals(radio1)) {
((RadioButton) newValue).setTextFill(Color.RED);
} else if (newValue.equals(radio2)) {
((RadioButton) newValue).setTextFill(Color.BLUE);
} else if (newValue.equals(radio3)) {
((RadioButton) newValue).setTextFill(Color.GREEN);
}
});
I need to temporarly change the background color of a clicked button for 0.5 seconds and then want it to revert back to the original color.
I have tried using pausetransition but im really new to java and am not sure how to use it correctly. Right now the best I can do is get he button to stay at the new color when clicked.
Color[] colors = new Color[]{Color.DARKORCHID,Color.SALMON,Color.SPRINGGREEN,Color.GOLD};
Color randomColor = colors[(new Random().nextInt(4))];
button.setBackground(new Background(new BackgroundFill(randomColor,null,null)));
grid.add(button, column, row);
button.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent e){
Button b = (Button)e.getSource();
Button save = b;
b.setBackground(new Background(new BackgroundFill(Color.LIGHTGRAY,null,null)));
}
});
}
This right now is just changing the color to grey. If I could figure out how to get the color to temporarily change OR even change back when clicked again. Another part of my issue is that all the buttons have different colors.
Any tips or help would be appreciated.
For each Button you create you need to create a PauseTransition, with a duration of half a second, that will set the background back to the original. When you click on the Button you would change the background to the grey background and restart the PauseTransition. This will make it so the background is reverted half a second after the last click. Here's a small example:
import java.util.Random;
import javafx.animation.Animation.Status;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
GridPane root = new GridPane();
root.setAlignment(Pos.CENTER);
root.setHgap(10);
root.setVgap(10);
root.setPadding(new Insets(10));
fillGrid(root);
primaryStage.setScene(new Scene(root, 500, 300));
primaryStage.show();
}
private static void fillGrid(GridPane grid) {
Background[] backgrounds = {
new Background(new BackgroundFill(Color.DARKORCHID, null, null)),
new Background(new BackgroundFill(Color.SALMON, null, null)),
new Background(new BackgroundFill(Color.SPRINGGREEN, null, null)),
new Background(new BackgroundFill(Color.GOLD, null, null))
};
Background greyBg = new Background(new BackgroundFill(Color.GREY, null, null));
Random rand = new Random();
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
Background original = backgrounds[rand.nextInt(backgrounds.length)];
Button button = new Button(String.format("(%d,%d)", i, j));
button.setBackground(original);
PauseTransition transition = new PauseTransition(Duration.seconds(0.5));
transition.setOnFinished(event -> button.setBackground(original));
button.setOnMouseClicked(event -> {
event.consume();
button.setBackground(greyBg);
transition.playFromStart();
});
grid.add(button, j, i);
}
}
}
}
I have a javafx application with multiple textboxes that the user can enter information in. I also have a keyboard built into the application that when pressed adds that text to the textbox.
My issue is that since I have multiple textboxes, I don't know which one to add the buttons text to. Is there a way in javafx to check if a user has clicked on a certain textbox so I can check which one has been selected and add the text there?
You can use the Scene.focusOwner property of the active scene to get the focused node. Check, if it's a TextInputControl and call the appropriate method for the button clicked. Note that clicking a button may move the focus, if focusTraversable is true for that button. (By default this is the case.)
#Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
final Scene scene = new Scene(grid);
for (int i = 0; i < 4; i++) {
grid.add(new TextField(), 0, i);
final String buttonValue = Character.toString((char) ('a'+i));
Button button = new Button(buttonValue);
button.setFocusTraversable(false); // prevent buttons from stealing focus
button.setOnAction(evt -> {
Node fo = scene.getFocusOwner();
if (fo instanceof TextInputControl) {
((TextInputControl) fo).replaceSelection(buttonValue);
}
});
grid.add(button, 1, i);
}
primaryStage.setScene(scene);
primaryStage.show();
}
You should create a listener for each TextField's focusProperty and set an instance variable.
Once you have a global reference to the currently focused TextField, you can do any processing on it that you choose.
Here is a quick application to demonstrate. I've included a couple extra details in the code itself:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
// Instance variable to hold the currently-selected TextField
private TextField selectedTextField;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Create TextFields
TextField txt1 = new TextField();
TextField txt2 = new TextField();
TextField txt3 = new TextField();
TextField txt4 = new TextField();
// This method sets the same change listener on each textfield
installListener(txt1, txt2, txt3, txt4);
VBox pane = new VBox(5);
pane.setPadding(new Insets(5));
// Add the TextFields to the layout
pane.getChildren().addAll(
new HBox(5, new Label("Txt1: "), txt1),
new HBox(5, new Label("Txt2: "), txt2),
new HBox(5, new Label("Txt3: "), txt3),
new HBox(5, new Label("Txt4: "), txt4)
);
primaryStage.setScene(new Scene(pane));
primaryStage.show();
}
// Accepts multiple TextFields
private void installListener(TextField... textFields) {
// Install the same listener on all of them
for (TextField textField : textFields) {
textField.focusedProperty().addListener((observableValue, oldValue, newValue) -> {
// Set the selectedTextField to null whenever focus is lost. This accounts for the
// TextField losing focus to another control that is NOT a TextField
selectedTextField = null;
if (newValue) {
// The new textfield is focused, so set the global reference
selectedTextField = textField;
System.out.println("Selected Text: " + selectedTextField.getText());
}
});
}
}
}
I have a Class that extends the CustomMenuItem. This MenuItems are added to a ContextMenu. Now i need to get the X-Coordinates from the right side of the CustomMenuItem.
The Problem is, that I have no idea how I can get the Coordinates.
The CustMenuItem has no function for getting the Coordinates like getX() or getY().
So how can I solve this problem?
This thing I would like to get:
Here we can see a Sample for a Context Menu (red lines). In the Context Menu are a lot of different CustomMenuItems implemented. Now I would like to get the right top corner Coordinate of the CustomMenuItem.
Thank you for your very nice help.
Before dealing with menu items, let's start saying that a ContextMenu is a popup window, so it has Windowproperties. You can ask for (x,y) left, top origin, and for (w,h).
But you have to take into account the effects, since by default it includes a dropshadow. And when it does, there's an extra space added of 24x24 pixels to the right and bottom.
.context-menu {
-fx-effect: dropshadow( gaussian , rgba(0,0,0,0.2) , 12, 0.0 , 0 , 8 );
}
Since this default dropshadow has a radius of 12px, and Y-offset to the bottom of 8px, the right and bottom coordinates of the context menu, including the 24x24 area, are given by:
X=t.getX()+cm.getWidth()-12-24;
Y=t.getY()+cm.getHeight()-(12-8)-24;
where t could be a MouseEvent relative to the scene, and values are hardcoded for simplicity.
Let's see this over an example. Since you don't say how your custom menu items are implemented, I'll just create a simple Menu Item with graphic and text:
private final Label labX = new Label("X: ");
private final Label labY = new Label("Y: ");
#Override
public void start(Stage primaryStage) {
final ContextMenu cm = new ContextMenu();
MenuItem cmItem1 = createMenuItem("mNext", "Next Long Option",t->System.out.println("next"));
MenuItem cmItem2 = createMenuItem("mBack", "Go Back", t->System.out.println("back"));
SeparatorMenuItem sm = new SeparatorMenuItem();
cm.getItems().addAll(cmItem1,cmItem2);
VBox root = new VBox(10,labX,labY);
Scene scene = new Scene(root, 300, 250);
scene.setOnMouseClicked(t->{
if(t.getButton()==MouseButton.SECONDARY || t.isControlDown()){
// t.getX,Y->scene based coordinates
cm.show(scene.getWindow(),t.getX()+scene.getWindow().getX()+scene.getX(),
t.getY()+scene.getWindow().getY()+scene.getY());
labX.setText("Right X: "+(t.getX()+cm.getWidth()-12-24));
labY.setText("Bottom Y: "+(t.getY()+cm.getHeight()-4-24));
}
});
scene.getStylesheets().add(getClass().getResource("root.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setTitle("Scene: "+scene.getWidth()+"x"+scene.getHeight());
}
private MenuItem createMenuItem(String symbol, String text, EventHandler<ActionEvent> t){
MenuItem m=new MenuItem(text);
StackPane g=new StackPane();
g.setPrefSize(24, 24);
g.setId(symbol);
m.setGraphic(g);
m.setOnAction(t);
return m;
}
If you remove the effect:
.context-menu {
-fx-effect: null;
}
then these coordinates are:
X=t.getX()+cm.getWidth();
Y=t.getY()+cm.getHeight();
Now that we have the window, let's go into the items.
MenuItem skin is derived from a (private) ContextMenuContent.MenuItemContainer class, which is a Region where the graphic and text are layed out.
When the context menu is built, all the items are wrapped in a VBox, and all are equally resized, as you can see if you set the border for the item:
.menu-item {
-fx-border-color: black;
-fx-border-width: 1;
}
This is how it looks like:
So the X coordinates of every item on the custom context menu are the same X from their parent (see above, with or without effect), minus 1 pixel of padding (by default).
Note that you could also go via private methods to get dimensions for the items:
ContextMenuContent cmc= (ContextMenuContent)cm.getSkin().getNode();
System.out.println("cmc: "+cmc.getItemsContainer().getBoundsInParent());
Though this is not recommended since private API can change in the future.
EDIT
By request, this is the same code removing lambdas and css.
private final Label labX = new Label("X: ");
private final Label labY = new Label("Y: ");
#Override
public void start(Stage primaryStage) {
final ContextMenu cm = new ContextMenu();
MenuItem cmItem1 = createMenuItem("mNext", "Next Long Option",action);
MenuItem cmItem2 = createMenuItem("mBack", "Go Back", action);
SeparatorMenuItem sm = new SeparatorMenuItem();
cm.getItems().addAll(cmItem1,cmItem2);
VBox root = new VBox(10,labX,labY);
Scene scene = new Scene(root, 300, 250);
scene.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
if(t.getButton()==MouseButton.SECONDARY || t.isControlDown()){
// t.getX,Y->scene based coordinates
cm.show(scene.getWindow(),t.getX()+scene.getWindow().getX()+scene.getX(),
t.getY()+scene.getWindow().getY()+scene.getY());
labX.setText("Right X: "+(t.getX()+cm.getWidth()-12-24));
labY.setText("Bottom Y: "+(t.getY()+cm.getHeight()-4-24));
}
}
});
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setTitle("Scene: "+scene.getWidth()+"x"+scene.getHeight());
}
private MenuItem createMenuItem(String symbol, String text, EventHandler<ActionEvent> t){
MenuItem m=new MenuItem(text);
StackPane g=new StackPane();
g.setPrefSize(24, 24);
g.setId(symbol);
SVGPath svg = new SVGPath();
svg.setContent("M0,5H2L4,8L8,0H10L5,10H3Z");
m.setGraphic(svg);
m.setOnAction(t);
return m;
}
private final EventHandler<ActionEvent> action = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("action");
}
};