JavaFX - Drawing a line between two nodes - java

I am busying writing a simple application using JavaFX2. The goal is just to plot 2 nodes (the nodes are movable by dragging them) and then have a function to draw lines between these nodes.
I finished the functions to add and move nodes (at the moment I am just using Ellipse shapes but I am going to replace it later with my own node class) but now I am struggling with the connecting lines. The actions to add a node or a line is from a dropdown menu and I have the following code on the line function:
private void drawLine(MenuItem line) {
final BooleanProperty lineActive = new SimpleBooleanProperty(false);
final BooleanProperty clickOne = new SimpleBooleanProperty(false);
final BooleanProperty clickTwo = new SimpleBooleanProperty(false);
line.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
lineActive.set(true);
}
});
nodeGroup.setOnMousePressed(new EventHandler<MouseEvent>() {
public void handle(final MouseEvent t1) {
clickOne.set(true);
if (lineActive.get()) {
if (clickOne.get()) {
//get x and y of first node
x1 = ((Ellipse) t1.getTarget()).getCenterX();
y1 = ((Ellipse) t1.getTarget()).getCenterY();
clickOne.set(false);
clickTwo.set(true);
}
if (clickTwo.get()) {
nodeGroup.setOnMouseClicked(new EventHandler<MouseEvent>() {
public void handle(MouseEvent t2) {
//get x and y of second node
x2 = ((Ellipse) t2.getTarget()).getCenterX();
y2 = ((Ellipse) t2.getTarget()).getCenterY();
//draw line between nodes
final Line line = new Line();
line.setStartX(x1);
line.setStartY(y1);
line.setEndX(x2);
line.setEndY(y2);
canvas.getChildren().add(line);
clickTwo.set(false);
lineActive.set(false);
}
});
}
}
}
});
}
I just have the booleans to check for the first and second click to get the center of each node.
My first question is when I click on the line function and add a line between 2 nodes, it doesn't seem to end the function, and any other nodes I click on gets a line to it. How can I prevent it from executing more than once.
And my second question is how can I "connect" the line to the nodes that if the node moves, the line stays in the center of the node?
Thanks.

I think there is a couple of things which could make this simpler...
Do not use booleans when you have more than two states (no click, click one, click two) use an enum instead. Then you only need one variable to look after.
Only ever set one mouse listener on the nodeGroup and check which state you're in and have the appropriate code there instead of separate mouse listeners.
I imagine that the program is setting the listener for the second click and not resetting it to the listener for the first click when it completes.

Since i am new to Stack Overflow , I tried something on your problem
First add line between two labels
final Line line = new Line();
#Override
public void initialize(URL arg0, ResourceBundle arg1)
{
// TODO Auto-generated method stub
line.setStartX(lblDragTest.getLayoutX());
line.setStartY(lblDragTest.getLayoutY());
line.setEndX(lblNew.getLayoutX());
line.setEndY(lblNew.getLayoutY());
rootAnchorPane.getChildren().add(line);
}
And then add this methods...
// This code handles label move
//set lblDragMousePressed method to Mouse Pressed event for lblDrag
#FXML
public void lblDragMousePressed(MouseEvent m)
{
System.out.println("Mouse is pressed");
prevLblCordX= (int) lblDragTest.getLayoutX();
prevLblCordY= (int) lblDragTest.getLayoutY();
prevMouseCordX= (int) m.getX();
prevMouseCordY= (int) m.getY();
}
//set this method on mouse released event for lblDrag
#FXML
public void lblDragMouseReleased(MouseEvent m)
{
System.out.println("Label Dragged");
}
// set this method on Mouse Drag event for lblDrag
#FXML
public void lblDragMouseDragged(MouseEvent m)
{
diffX= (int) (m.getX()- prevMouseCordX);
diffY= (int) (m.getY()-prevMouseCordY );
int x = (int) (diffX+lblDragTest.getLayoutX()-rootAnchorPane.getLayoutX());
int y = (int) (diffY+lblDragTest.getLayoutY()-rootAnchorPane.getLayoutY());
if (y > 0 && x > 0 && y < rootAnchorPane.getHeight() && x < rootAnchorPane.getWidth())
{
lblDragTest.setLayoutX(x);
lblDragTest.setLayoutY(y);
}
line.setStartX(lblDragTest.getLayoutX());
line.setStartY(lblDragTest.getLayoutY());
line.setEndX(lblNew.getLayoutX());
line.setEndY(lblNew.getLayoutY());
// rootAnchorPane.getChildren().add(line);
}

Related

I want to get coordinates of x and y using mouse adapter and insert into an array. But only two values are getting updated every time in whole array

I want to enter the X and Y coordinates of mouse adapter to array . But whenever I insert them into array using for loop, the whole array is getting filled with same value. How can I insert X and Y coordinates to the array every time the JPanel is clicked?
Code:
addMouseListener(new MouseAdapter () {
public void mouseClicked(MouseEvent e) {
setBackground(Color.RED);
int k[]=new int[18];
int l[]=new int[18];
for(int i=0;i<=17;i++) {
k[i]=e.getX();
l[i]=e.getY();
}
}
});
It's not clear from the OP where the data is meant to be going. Assuming it's being used elsewhere then -
// NOTE: perhaps use CopyOnWriteArrayList or synchronize all
// use of clicks for MT safety..
final List<Point> clicks = new ArrayList<Point>();
X.addMouseListener(new MouseAdapter () {
public void mouseClicked(MouseEvent e) {
setBackground(Color.RED);
clicks.add(e.getPoint());
}
});
Each time the mouse is clicked another point is added to the clicks array.
HT to #AndrewThompson

Java Swing Double-click + drag to select whole words

In most text editors and platforms there are two ways of selecting text using the mouse:
The regular, Click+Drag, moves the end of the selection along with the mouse cursor
Double-click+Drag, same as #1 but it starts by selecting one whole word, and then snaps the end of the selection to whole words
In Swing GUI however, #2 does not work as above. It starts correctly by selecting the whole word where the double-click was, but then it does not snap to whole words during selection.
Is there any way to get Swing text fields to behave as 2, with the selection snapping to whole words?
You can create a method to calculate the index of where word your selection ends and starts. See below:
int getWordEndPos(String text, int initPos) {
int i = initPos;
while(Character.isAlphabetic(text.charAt(i))) {
i++;
}
return i;
}
int getWordStartPos(String text, int initPos) {
int i = initPos;
while(Character.isAlphabetic(text.charAt(i))) {
i--;
}
return i+1;
}
Then in your UI (not sure exactly how JTextArea works) you could get the start and end position of your selection, and actually selects the start and end position of their words:
void updateSelection(JTextArea ta) {
String text = ta.getText();
int start = ta.getSelectionStart();
int end = ta.getSelectionEnd();
start = getWordStartPos(text, start);
end = getWordEndPos(text, end);
ta.select(start, end);
}
But where to call the snippet above? You could listen to CarretEvent instead of MouseEvent (see Which event a selection of text trigger in Java JTextArea?):
textArea.addCarretListener((evt) -> updateSelection(textArea));
But another problem arrises: how to know the click count of MouseEvent. You could make an attribute to store it, and then into the mouse event listener, it can be set. The code below tries to put everything toghether:
class UI implements MouseListener, CarretListener {
JTextArea textArea;
int clickCount = 0;
UI() {
textArea.addCarretListener(this);
textArea.addMouseListener(this);
// ...
}
#Override
void mouseClicked(MouseEvent evt) {
this.clickCount = evt.getClickCount();
// other stuff
}
// other MouseListener methods
#Override
void caretUpdate(CaretEvent evt) {
if (clickCount == 1) updateSelection(textArea);
// other caret listener stuff
}
void updateSelection(JTextArea ta) {
String text = ta.getText();
int start = ta.getSelectionStart();
int end = ta.getSelectionEnd();
start = getWordStartPos(text, start);
end = getWordEndPos(text, end);
ta.select(start, end);
}
}

JavaFX rectangle mouseonclick grid return

I am writing a board game which has a 20x20 grid.
This is in my board class:
private final Position[][] grid = new Position[GRID_SIZE][GRID_SIZE];
each position has :
public class Position {
private final Coordinates pos;
private Player player;
private final static double RECTANGLE_SIZE = 40.0;
private final Rectangle rect = new Rectangle(RECTANGLE_SIZE, RECTANGLE_SIZE);
}
so basically I have 20x20 Positions and each positions has a Rectangle
This is what I do to display the grid
for (int cols = 0; cols < GRID_SIZE; ++cols) {
for (int rows = 0; rows < GRID_SIZE; ++rows) {
grid.add(gameEngine.getBoard().getGrid()[cols][rows].getRect(), cols, rows);
}
}
Anyway, the grid is initialized and works properly. What I want to do is to make the rectangle objects clickable and to be able to return their Coordinates when they are clicked.
This is how I handle the mouse click
private void setUpRectangle() {
rect.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
rect.setFill(Color.BLACK);
}
});
}
What this code does is to change the color of the rectangle to black, but how could I return the Coordinates.
Basically, I can edit the onclick function to return the coordinates of this position, but how can I acquire them later?
This is not a JavaFX question as much as it is a design question. You have a container (Position) of 2 objects (Coordinates and Rectangle) and you want one of them to know about the other. That is, the rectangle should know the coordinates of its position.
There are a few approaches here, and depending on the bigger picture, one might be better than the others. James_D mentioned a couple in a comment.
Keep a reference of the position object in the rectangle object. This is useful if rectangle needs to access various datum in the container from various places. You would do something like rectangle.getPosition().getCoordinates() or .getPlayer().
Keep a reference of the coordinates object in the rectangle object. This is a more specific approach of 1 useful if you only need that object. You would do something like rectangle.getCoordinates().
Pass the coordinates to your setUpRectangle method. This is useful if you rectangle doesn't need access to this data from various places, it's a local solution. Then in the handle method you would return the coordinates you passed to setUpRectangle, though we can't see what class this method is in.
Use external help. You can keep something like Map<Rectangle, Coordinates> and then call map.get(rectangle). You can hide this map in a method Coordinates getCoordinatesForRectangle(Rectangle rectangle) instead of calling it directly.
You could store this data as userData (or use properties in case userData is preserved for something else in your program):
private final Rectangle rect;
public Position() {
rect = new Rectangle(RECTANGLE_SIZE, RECTANGLE_SIZE);
rect.setUserData(this);
}
rect.setOnMouseClicked((MouseEvent event) -> {
Position pos = (Position) ((Node) event.getSource()).getUserData();
...
});
You could also use a listener that knows about the position:
class CoordinateAwareListener implements EventHandler<MouseEvent> {
private final int coordinateX;
private final int coordinateY;
public CoordinateAwareListener(int coordinateX, int coordinateY) {
this.coordinateX = coordinateX;
this.coordinateY = coordinateY;
}
#Override
public void handle(MouseEvent event) {
// do something with the coordinates
}
}

Coordinates of children inside a FlowPane (JavaFX)

I'm attempting to write a GUI that presents five rectangles in a FlowPane, so that the user may click on them to select a character.
I use the following method to test whether the user has clicked inside a rectangle or not:
#FXML
private Rectangle characterA, characterB, characterC, characterD, character E;
private List<Rectangle> rects;
#FXML
private void initialize() {
rects = Arrays.asList(characterA, characterB, characterC, characterD, character E);
}
#FXML
private void onMousePressed(MouseEvent e) {
Rectangle rect;
for (int i = 0; i < rects.size(); i++) {
rect = rects.get(i);
if (rect.contains(e.getSceneX(), e.getSceneY())) {
System.out.println("Clicked in rect " + i);
}
}
}
My problem is this: although on screen, the rectangles are laid out alongside each other, their coordinates are all apparently the same. Clicking on the first rectangle results in five copies of "Clicked in rect x", whilst clicking elsewhere gives no output at all.
How can I correctly determine which rectangle the user clicked in?

MouseListener called multiple times

I am using this code to get the X and Y coordinates of an image placed as icon of a jLable.
This method to get the coordinates was suggested by an answer to this question.
private void lblMapMouseClicked(java.awt.event.MouseEvent evt) {
lblMap.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
double X = e.getX();
double Y = e.getY();
System.out.println("X: " + X + "Y: " + Y );
}
});
}
When I run this public void mouseClicked(MouseEvent e) { } gets called multiple times.
Exactly the amount of times I click on the image.
Eg: If I'm clicking on it for the 3rd time ,
X and Y values from the System.out.println line , gets printed 3 times.
And it increases as the number of times I click increases.
Can any of you explain why this happens? And how can I fix it? :)
The problem is that you are adding a new listener again and again when click happens, here.
private void lblMapMouseClicked(MouseEvent evt)
{
lblMap.addMouseListener(new MouseAdapter()
{
...
Instead, change your code to this.
private void lblMapMouseClicked(MouseEvent e)
{
double X = e.getX();
double Y = e.getY();
System.out.println("X: " + X + "Y: " + Y);
}
And it should fix the problem.
Hope this helps.
it looks for me that every time image is clicked new mouse listener is added.. do also
System.out.println(this)
to check from which instance of mouse listener it is actually printed
The problem with above code was you are creating new Mouse event with every click on the image.
// Create a Mouse pressed Event
mouseLis = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
actionMenthod(e);
}
};
Here am attaching the my event to lblMap.
lblMap.addMouseListener(mouseLis);
After this event happens you have to remove this event from the lblmap.
lblMap.removeMouseListener(mouseLis);
After when I click again only one event will be there then it prints only once.

Categories

Resources