JavaFX, MouseEvent problems - java

I am currently making a paint application and have created several tools which are working, but I encountered a problem when trying to create a
"Draw straight line" tool
So I basically draw a line from point A to B and it works, the line is there, however, when I toggle my other tools (Draw circle, rectangle etc) the shapes are being drawn at the same time as the straight-line despite the "Draw Line" button being toggled off.
The code below will allow you to draw straight-lines and you can try toggling on and off the different buttons, the straight line will still be drawn when you drag the cursor across the pane.
Anyone know what kind of mistake I did, and any possible fixes and/or alternate solutions?
(The event handler is there so that I can select the drawn shapes change them later if needed, this code is a stripped-down version of my paint application)
public class DrawLine extends Application {
#Override
public void start(Stage primaryStage) {
ToggleButton lineButton = new ToggleButton ("Draw Line");
ToggleButton Button = new ToggleButton ("Button with no function");
BorderPane pane = new BorderPane();
ToolBar toolbar = new ToolBar();
Scene scene = new Scene(pane, 1200, 800);
pane.setLeft(toolbar);
toolbar.getItems().addAll(lineButton, Button);
// Draw Line
scene.addEventHandler(MouseEvent.MOUSE_CLICKED, me -> {
if(lineButton.isSelected() & me.getButton().equals(MouseButton.PRIMARY) ) {
scene.setOnMousePressed(event -> {
Line line = new Line();
line.setStartX(event.getX());
line.setStartY(event.getY());
scene.setOnMouseDragged(e->{
line.setEndX(e.getX());
line.setEndY(e.getY());
});
pane.getChildren().add(line);
});
}
});
primaryStage.setTitle("Paint App");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}

You only check if the lineButton is selected inside the MOUSE_CLICKED (which is a press-then-release gesture, by the way) handler. Inside this handler you add a MOUSE_PRESSED handler and inside that handler you add a MOUSE_DRAGGED handler. You don't check if the lineButton is selected inside the MOUSE_PRESSED or MOUSE_DRAGGED handlers.
What this all means is that, after the if condition inside the MOUSE_CLICKED handler evaluates to true, you'll have a MOUSE_PRESSED and MOUSE_DRAGGED handler that operate independently of your MOUSE_CLICKED handler. Now, whenever you press any mouse button it will create a Line and add it to the parent. Then the newly added MOUSE_DRAGGED handler will alter the Line.
You're fortunate, in a way, that you're using the onXXX properties instead of using addEventHandler. The properties replace the old EventHandler when set. If that didn't happen (such as with addEventHandler) you'd have many (one more each time) EventHandlers drawing Lines.
You just need to register all the appropriate EventHandlers once and do the logic inside of them.
Here's a small example:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.ToolBar;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class Main extends Application {
private ToggleGroup toggleGroup;
private ToggleButton lineBtn;
private Group group;
private Line currentLine;
#Override
public void start(Stage primaryStage) {
toggleGroup = new ToggleGroup();
lineBtn = new ToggleButton("Line");
ToggleButton noneBtn = new ToggleButton("None");
toggleGroup.getToggles().addAll(lineBtn, noneBtn);
toggleGroup.selectToggle(noneBtn);
group = new Group();
BorderPane root = new BorderPane(new Pane(group), new ToolBar(lineBtn, noneBtn), null, null, null);
root.getCenter().setOnMousePressed(this::handleMousePressed);
root.getCenter().setOnMouseDragged(this::handleMouseDragged);
root.getCenter().setOnMouseReleased(this::handleMouseReleased);
primaryStage.setScene(new Scene(root, 800, 600));
primaryStage.setTitle("Draw Shape Example");
primaryStage.show();
}
private void handleMousePressed(MouseEvent event) {
if (lineBtn.equals(toggleGroup.getSelectedToggle())
&& event.getButton() == MouseButton.PRIMARY) {
currentLine = new Line(event.getX(), event.getY(), event.getX(), event.getY());
group.getChildren().add(currentLine);
}
}
private void handleMouseDragged(MouseEvent event) {
if (currentLine != null) {
currentLine.setEndX(event.getX());
currentLine.setEndY(event.getY());
}
}
private void handleMouseReleased(MouseEvent event) {
if (currentLine != null
&& currentLine.getStartX() == currentLine.getEndX()
&& currentLine.getStartY() == currentLine.getEndY()) {
// The line has no length, remove it
group.getChildren().remove(currentLine);
}
currentLine = null;
}
}

Related

JavaFX mouse drag events not firing

I tried almost everything, but the mouse drag events are not firing, like explained here:
https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/input/MouseDragEvent.html
Here is a minimal example, so you can try it out (I am using Java 11 with JavaFX 11.0.2):
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class Main extends Application {
private double mouseClickPositionX, mouseClickPositionY, currentRelativePositionX, currentRelativePositionY;
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Hello World");
BorderPane mainBorderPane = new BorderPane();
BorderPane centerBorderPane = new BorderPane();
FlowPane flowPane = new FlowPane();
GridPane gridPane = new GridPane();
Button button1 = new Button("button1");
gridPane.add(button1, 0, 0);
flowPane.getChildren().add(gridPane);
centerBorderPane.setCenter(flowPane);
HBox hbox = new HBox();
TilePane tilePane = new TilePane();
Button button2 = new Button("button2");
tilePane.getChildren().add(button2);
hbox.getChildren().add(tilePane);
mainBorderPane.setCenter(centerBorderPane);
centerBorderPane.setBottom(hbox);
// button2 event handlers
button2.setOnMousePressed(event -> {
mouseClickPositionX = event.getSceneX();
mouseClickPositionY = event.getSceneY();
currentRelativePositionX = button2.getTranslateX();
currentRelativePositionY = button2.getTranslateY();
button2.setMouseTransparent(true);
});
button2.setOnMouseDragged(event -> {
button2.setTranslateX(currentRelativePositionX + (event.getSceneX() - mouseClickPositionX));
button2.setTranslateY(currentRelativePositionY + (event.getSceneY() - mouseClickPositionY));
});
button2.setOnDragDetected(event -> {
button2.startFullDrag();
});
button2.setOnMouseReleased((event) -> {
button2.setMouseTransparent(false);
});
// button1 event handlers
button1.setOnMouseDragReleased((event) -> {
System.out.println("it works in button1");
});
// gridPane event handlers
gridPane.setOnMouseDragReleased((event) -> {
System.out.println("it works in gridPane");
});
primaryStage.setScene(new Scene(mainBorderPane, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I want to get the reference of button2 either in button1 or in gridPane via setOnMouseDragReleased. There are many nested panes etc. because I wanted to maintain the original project layout structure. I did this because I am not sure if this also can be a reason for the non functioning.
Thanks in advance.
I've ended up manually triggering the events from centerBorderPane to gridPane, using node.fireEvent(event). Also implemented a helper function, which returns the right child node:
private Optional<Node> findNode(Pane pane, double x, double y) {
return pane.getChildren().stream().filter(n -> {
Point2D point = n.sceneToLocal(x, y);
return n.contains(point.getX(), point.getY());
}).findAny();
}
Don't forget to consume the events, so you won't get into an infinite loop.
MOUSE_DRAG_RELEASED fires when a drag ends on this node. For example
centerBorderPane.setOnMouseDragReleased((event) -> {
System.out.println("centerBorderPane drag released");
});
should fire when you drag button2 and the drag ends on centerBorderPane.
To fire an event when the mouse is dragged over button1 use button1.setOnMouseDragged
If you want to propagate a mouse event from parent to its children see this

How to get Node while mouse dragged by event position in JavaFX 8?

I have created code example:
package stackoverflow;
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class GetNodeByMousePositionWhileDragged extends Application {
private Line line;
private Group group = new Group();
#Override
public void start(Stage stage) throws Exception {
AnchorPane anchorPane = new AnchorPane();
Circle source = new Circle(30, Color.LIGHTGREEN);
source.setStroke(Color.BLACK);
source.setCenterX(100);
source.setCenterY(100);
source.setOnMousePressed(me -> {
me.consume();
drawLine(source, me);
});
source.setOnMouseDragged(me -> translateLineEnd(getPoint(me)));
source.setOnMouseReleased(event -> group.getChildren().remove(line));
Circle target = new Circle(30, Color.LIGHTBLUE);
target.setStroke(Color.BLACK);
target.setCenterX(400);
target.setCenterY(100);
target.setOnMousePressed(me -> {
me.consume();
drawLine(target, me);
});
target.setOnMouseDragged(me -> translateLineEnd(getPoint(me)));
target.setOnMouseReleased(event -> group.getChildren().remove(line));
group.getChildren().addAll(source, target);
anchorPane.getChildren().addAll(group);
stage.setScene(new Scene(anchorPane, 600, 400));
stage.setMaximized(true);
stage.show();
}
private void drawLine(Circle source, MouseEvent me) {
line = new Line();
line.setStroke(Color.BLACK);
line.setStrokeWidth(1);
line.startXProperty().bind(source.centerXProperty());
line.startYProperty().bind(source.centerYProperty());
translateLineEnd(getPoint(me));
group.getChildren().add(line);
}
private void translateLineEnd(Point2D point) {
line.setEndX(point.getX());
line.setEndY(point.getY());
}
private Point2D getPoint(MouseEvent me) {
return new Point2D(me.getSceneX(), me.getSceneY());
}
}
Here I am just adding two circles and I want to connect them with a line by simply dragging from one circle to another. But the problem is that I want to verify whether mouse entered target circle while I am dragging mouse from source circle. When it is entered I want just bind end points of the line to the target circle center points or remove line on mouse released if it is not entered any circle besides source one.
Unfortunately while dragging one circle another one is not catching mouseevents. But it is possible to get mouse position on scene. I tried to solve this problem by simply storing all circle (I have bunch of them, 10K+), iterating each time and checking circle.contains(me.getSceneX(), me.getSceneY()). It seems to me a bit expensive way or like inventing wheel.
There is a question is it possible in JavaFX 8 to get node according scene position in proper way by using built-in JavaFX features?
You need to modify the code a bit:
Use MouseDragEvents by calling startFullDrag for the source node in the onDragDetected event.
Set mouseTransparent to true for the line to allow JavaFX to deliver the MouseEvents to the target circle instead of the Line.
Modify the event handlers to yield different results, if the mouse was released on the target circle.
private void drawLine(Circle source, MouseEvent me) {
line = new Line();
line.setMouseTransparent(true);
...
private Group group = new Group();
private boolean removeLine = true;
source.setOnMousePressed(me -> {
me.consume();
drawLine(source, me);
me.setDragDetect(true); // trigger dragDetected event immediately
});
source.setOnDragDetected(evt -> {
source.startFullDrag();
removeLine = true;
});
...
source.setOnMouseReleased(event -> {
if (removeLine) {
group.getChildren().remove(line);
}
});
target.setOnMouseDragReleased(me -> removeLine = false);

JavaFX Split Menu Button Arrow Trigger Event

I have a SplitMenuButton, and I can't seem to find a way to trigger an event when the user clicks the arrow next to the button.
I would like the dropdown to fill with items from a database when the dropdown arrow is clicked.
I am not sure which event can do that, and I can not find any info on this either.
Short answer: register a listener with the showing property.
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SplitMenuButton;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class SplitMenuButtonTest extends Application {
#Override
public void start(Stage primaryStage) {
IntegerProperty count = new SimpleIntegerProperty();
SplitMenuButton splitMenuButton = new SplitMenuButton();
splitMenuButton.setText("Action");
splitMenuButton.showingProperty().addListener((obs, wasShowing, isNowShowing) -> {
if (isNowShowing) {
int c = count.get() + 1;
count.set(c);
splitMenuButton.getItems().clear();
for (int choice = 1; choice <= 3; choice++) {
MenuItem mi = new MenuItem("Choice "+choice+" (" + c + ")");
splitMenuButton.getItems().add(mi);
}
}
});
BorderPane root = new BorderPane(null, splitMenuButton, null, null, null);
primaryStage.setScene(new Scene(root, 350, 150));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Sort of as an aside, I'm not sure this is a really good idea. Database connections are typically long-running processes (i.e. long enough to be visually noticeable in a UI environment). If you run this on the FX Application Thread, then you're going to block the UI from doing anything while the data is retrieved, and that's also right at the moment the user has just tried to do something. Of course, if you run it as a background task, then the menu will popup with the previous data, and then later update once the data is downloaded. I would recommend finding a way to populate this before the user requests it.
The 'arrow' is just another button used to show the popup with the menu items.
One easy way of knowing if this arrow button is pressed is by listening to the showing property of the popup.
Once you know that the popup is showing up, you can add your items.
#Override
public void start(Stage primaryStage) {
SplitMenuButton m = new SplitMenuButton();
m.showingProperty().addListener((ov,b,b1)->{
if(b1){
System.out.println("popup visible");
MenuItem menuItem = new MenuItem("New Option");
if(m.getItems().stream().noneMatch(i->i.getText().equals(menuItem.getText()))){
menuItem.setOnAction(e -> System.out.println("New Option added"));
m.getItems().add(menuItem);
}
}
});
m.setText("Click the arrow ->");
m.getItems().add(new MenuItem("First option"));
StackPane root = new StackPane(m);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}

How to make the MenuItems in a JavaFX context menu support an onMouseOver event

I'm looking for some guidance on how to proceed with a problem I'm having. I hava a JavaFX scene and within it some nodes (shapes) that connect to each other with one or more lines. I can right-click on a shape to bring up a context menu. Let's say this particular shape that was just right-clicked has 3 lines coming out of it (call them line1, line2, line3) and you want to use the context menu to delete one. You can select "line2" for example, and it will fire the onAction event to remove that line. That all works fine.
The trouble is, you don't know which of the 3 lines on the screen is line1 or line2 or line3 (unless of course they are labeled) and so you don't know which one you are about to remove until you remove it. What I would really like to do, for example, is to place my mouse over "line2" in the context menu and have line2 in the scene change color or something to indicate that it is the one about to be deleted (before I click the mouse). However, the only event I see supported by MenuItem is the onAction event for when it is clicked. Is there some way to give it onMouseOver functionality? if not, how could this feature be implemented?
Thanks!
Try this SSCCE:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ContextMenuDemo extends Application {
private DropShadow ds = new DropShadow();
#Override
public void start(Stage primaryStage) {
final Line line1 = new Line(60, 10, 150, 10);
final Line line2 = new Line(60, 30, 150, 50);
final Line line3 = new Line(60, 60, 150, 90);
final ContextMenu cm = new ContextMenu();
cm.getItems().add(getMenuItemForLine("line 1", line1));
cm.getItems().add(getMenuItemForLine("line 2", line2));
cm.getItems().add(getMenuItemForLine("line 3", line3));
final Rectangle rectangle = new Rectangle(70, 70, Color.TAN);
rectangle.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
if (e.getButton() == MouseButton.SECONDARY) {
cm.show(rectangle, e.getScreenX(), e.getScreenY());
}
}
});
Group root = new Group();
root.getChildren().addAll(rectangle, line1, line2, line3);
Scene scene = new Scene(root, 300, 250);
// load style of modified paddings for menuitems
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
private MenuItem getMenuItemForLine(String menuName, final Line line) {
Label menuLabel = new Label(menuName);
// apply style to occupy larger space for label
menuLabel.setStyle("-fx-padding: 5 10 5 10");
MenuItem mi = new MenuItem();
mi.setGraphic(menuLabel);
line.setStroke(Color.BLUE);
menuLabel.addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
line.setStroke(Color.RED);
line.setEffect(ds);
}
});
menuLabel.addEventHandler(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
line.setStroke(Color.BLUE);
line.setEffect(null);
}
});
return mi;
}
public static void main(String[] args) {
launch(args);
}
}
with style.css
.menu-item {
/* -fx-skin: "com.sun.javafx.scene.control.skin.MenuItemSkin";*/
-fx-background-color: transparent;
-fx-padding: 0em; /* do not pad for item. we want to ccupy all spaces for graphics only */
}
.menu-item:focused {
-fx-background: -fx-accent;
-fx-background-color: -fx-selection-bar;
-fx-text-fill: -fx-selection-bar-text;
}
.menu-item .graphic-container {
-fx-padding: 0em; /* do not pad for graphics, label graphic pads itself */
}
.menu-item .label {
-fx-padding: 0em; /* do not pad for label, since there is no label text set */
-fx-text-fill: -fx-text-base-color;
}
Screenshot:
Description:
This is somewhat a bug that MenuItem does not work for MenuItem.addEventHandler(MouseEvent.MOUSE_ENTERED, ...) I think. As a workaround, we define new Label, register event handlers to it and set it as a graphic of menu item while the text(label) of menuitem intentionally left an empty. But the graphic of menu item does not (by default) occupy all space of menu item, so mouse events are not handled properly at the edges of menu item. To overcome this problem we reset all paddings of menuitem, menuitem's label and graphic through css. You can observe this by commenting out the style loading in the above code.
Here is a sample App I just created on an aproach to identify the lines:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Tooltip;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class MainTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
// TODO Auto-generated method stub
AnchorPane anchorPane = new AnchorPane();
Scene scene = new Scene(anchorPane);
stage.setScene(scene);
Line linea = new Line(0, 0, 50, 50);
linea.setFill(Color.BLACK);
final Tooltip t = new Tooltip("Line 1");
linea.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Line line = (Line) event.getSource();
line.setStroke(Color.RED);
t.show((Line) event.getSource(), event.getScreenX(),
event.getScreenY());
}
});
linea.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Line line = (Line) event.getSource();
line.setStroke(Color.BLACK);
t.hide();
}
});
anchorPane.getChildren().add(linea);
stage.show();
}
}
Hope it helps!

Set ToolBar Buttons square and all the same size in JavaFX 2

In the code below I have a ToolBar and I add buttons of various sizes to it. I would like the buttons to be square and all the same size. So basically find the longest width or height from all the buttons, and set all other button widths and height to this size. However, the buttons can change size so I require a binding I think. I can't quite figure it out - anyone know how to do it?
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ToolBarButtonTest extends Application {
#Override
public void start(Stage stage) throws Exception {
BorderPane borderPane = new BorderPane();
Scene scene = new Scene(borderPane, 500, 500);
ToolBar toolBar = new ToolBar();
Button button1 = new Button("s");
Button button2 = new Button("ss");
Button button3 = new Button("sss");
toolBar.getItems().addAll(button1, button2, button3);
borderPane.setTop(toolBar);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Thanks, Nick.
Here you go. This example will use CSS to turn the button shape into a square, and install a validation listener to track layout changes to the buttons and update the widths accordingly.
public class SquareButtons extends Application {
#Override
public void start(final Stage stage) throws Exception {
/* Assume these are your toolbar elements */
final Button[] buttons = new Button[]{
new Button("Short"),
new Button("Slightly longer"),
new Button("Very very very long button")
};
/* This would, of course, belong in a style sheet - it turns the buttons square */
for (Button b : buttons)
b.setStyle("-fx-background-radius: 0");
/* This will set the pref width/height of all your buttons to the maximum of the pref width/height of the larget one */
final InvalidationListener listener = new InvalidationListener() {
public void invalidated(final Observable observable) {
double size = 0;
for (Button b : buttons) {
size = Math.max(size, b.prefWidth(Integer.MAX_VALUE));
size = Math.max(size, b.prefHeight(Integer.MAX_VALUE));
}
for (Button b : buttons) {
b.setPrefWidth(size);
b.setPrefHeight(size);
}
}
};
for (Button b : buttons)
b.widthProperty().addListener(listener);
final ToolBar toolbar = new ToolBar();
toolbar.getItems().addAll(buttons);
final Scene scene = new Scene(toolbar);
stage.setScene(scene);
stage.setWidth(800);
stage.setHeight(200);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
Update: Didn't read question thoroughly enough, see comment.
Since no one else has responded, I'll pass along a partial solution. I'm a noob at JavaFX, but since the parent is responsible for laying out the child nodes, I would derive a modified version of the ToolBar class and override some of the layout code. Here's is a modification of your example that works, but is a bit kludgy.
package toolbarbuttontest;
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ToolBarButtonTest extends Application {
#Override
public void start(Stage stage) {
BorderPane borderPane = new BorderPane();
Scene scene = new Scene(borderPane, 500, 500);
Button btn1 = new Button("short");
Button btn2 = new Button("between");
Button btn3 = new Button("pretty long");
SquareButtonToolBar toolBar = new SquareButtonToolBar();
toolBar.getItems().addAll(btn1, btn2, btn3);
borderPane.setTop(toolBar);
stage.setScene(scene);
stage.show();
toolBar.requestLayout();
}
// A derivative of the ToolBar class that will resize all buttons to be
// the same size (based on the length of the longest button label) and
// square.
class SquareButtonToolBar extends ToolBar {
#Override
protected void layoutChildren() {
double minPrefSize = calculatePrefChildSize();
for (Node n : getItems()) {
if (n instanceof Button) {
((Button) n).setPrefWidth(minPrefSize);
((Button) n).setPrefHeight(minPrefSize);
}
}
super.layoutChildren();
}
private double calculatePrefChildSize() {
double minPrefSize = 0.0d;
for (Node n : getItems()) {
if (n instanceof Button) {
minPrefSize = Math.max(minPrefSize, n.prefWidth(-1));
}
}
return minPrefSize;
}
}
public static void main(String[] args) {
launch(args);
}
}
The SquareButtonToolBar class overrides the layout code by setting the preferred width and height of the buttons to the length of the longest button, thus making all the buttons square and the same size. The kludge is the call to toolBar.requestLayout() at the bottom of the start method. Without this call, even though the button widths are all shown as desired on program start up, the heights are not displayed correctly until the window is re-sized. Not sure what I'm missing myself. If you find the answer, please post an update.
UPDATE: Modified example to a fully working one, but with a nasty kludge.

Categories

Resources