Differentiate between mouse drag and mouse click/released - java

Scenario. I have a graph on which i can perform panning using right click. This works perfectly. Then I added menus on right click perfectly.
Problem. Now right click menus are shown even on mouse release after drag operation completions.
Is there a way to differentiate mouse release and mouse release after mouse drag in Java Swing or JavaFX?

Mouse events are generated independently of one another.
I assume you panning code works with a combination of mousePressed/mouseMoved.
So you need to add some logic to indicate that you are in "panning mode". So if you have a mousePressed followed by a mouseMoved you set a Boolean variable to indicate "panning" mode.
Then in the mouseReleased code you need to check the variable. If "panning mode" then set "panning mode" off and return. Otherwise you are in "popup mode" so you can display the popup.

Since event.isDragDetect() is always true, so I was not able to differentiate events. I created a java class to store a boolean value. This was needed to modify final object state in inner class and was not possible using Boolean wrapper class. Later I am modifying final objects states based on mouse clicked and mouse dragged. I am also checking if mouse release is after drag or without drag as below:-
private void addRightClickMenu() {
final SubMarineBooleanUtilityClass showMenu = new SubMarineBooleanUtilityClass(true);
final MenuItem graphButton1 = new MenuItem("Save Graph as..");
graphButton1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
saveGraphAs();
}
});
final MenuItem graphButton2 = new MenuItem("Reset Graph");
graphButton2.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
resetGraph(controlGraph1.getLineChart(), controlGraph2.getLineChart());
}
});
final ContextMenu menu = new ContextMenu(graphButton1, graphButton2);
//Mouse Drag operation cycle=Mouse click+Mouse dragged+Mouse release
getAnchorPaneGraphView().setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton())) {
showMenu.setValueBoolean(true);
}
}
});
getAnchorPaneGraphView().setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton()) && showMenu.isValueBoolean()) {
menu.show(getAnchorPaneGraphView(), event.getScreenX(), event.getScreenY());
}
}
});
getAnchorPaneGraphView().setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton())) {
showMenu.setValueBoolean(false);
}
}
});
}
public class SubMarineBooleanUtilityClass {
boolean valueBoolean=false;
/**
* #return boolean
*/
public boolean isValueBoolean() {
return valueBoolean;
}
/**
* Constructor passing a boolean values
* #param value
*/
public SubMarineBooleanUtilityClass(boolean value){
this.valueBoolean=value;
}
/**set boolean value
* #param value
*/
public void setValueBoolean(boolean valueBoolean) {
this.valueBoolean = valueBoolean;
}
}

Related

Add mouse listener for all components in the java swing application

Task:
There is a large application in which new jComponent are periodically created. Need to add processing for all of them by mouse clicking on them with the "Alt" key pressed.
It is clear that this needs to be done using component.addMouseListener().
Is there a section of the JComponent creation code where we can insert our code?
Or are there other ways to solve the problem?
My code based on Rjelinek's suggestion:
public static void registrationMouseAction() {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(final AWTEvent event) {
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
if (mouseEvent.getID() == MOUSE_CLICKED) {
// # left prev
if (mouseEvent.getButton() == 4) {
new BackRecentDefinitionAction().performed();
}
// # right next
if (mouseEvent.getButton() == 5) {
new BackRecentDefinitionAction().performed();
}
// # view modal editor
if (mouseEvent.getButton() == 1 && mouseEvent.isAltDown()) {
new OpenModalEditorRadixObjectsAction().performed();
}
}
}
}
}, AWTEvent.MOUSE_EVENT_MASK);
}
Correction:
I need to have the processing of "Alt+MouseButton1" for all components in the application (for example, JButton, JPanel, etc.).
the solution for you lies somewhere in this lines. However I must say that on application start up it does make some mouse click on background, that will probably need to be addressed
EDIT
After posting I realized there is a request for alt key down, so I added this condition and it restrict code spam on start up. however its still not optimal
public class Main {
public static void main(String[] args) {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTListener(), AWTEvent.MOUSE_EVENT_MASK);
//your code
}
private static class AWTListener implements AWTEventListener{
#Override
public void eventDispatched(AWTEvent event) {
if (event instanceof MouseEvent evt && evt.isAltDown()) {
//your mouse event code
}
}
}

JavaFX setting Button style on Mouse Click

I've got a problem with a Java project I'm working on: I'm creating a grid of buttons via code in javafx on a pane. The buttons are all types of a subclass of the javafx Button class that i wrote.
Here's the header of the class:
private final String BASIC_STYLE = "-fx-font: 6 arial;";
private final String CLICKED_STYLE = "-fx-background-color: #0f0";
private int row;
private int col;
private String category;
private boolean selected = false;
Within the constructor i do the follwing:
this.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
toggleSelected();
}
});
Here's the toggleSelected() Method:
public void toggleSelected() {
this.selected = !selected;
this.setStyle(selected ? this.BASIC_STYLE : this.BASIC_STYLE+this.CLICKED_STYLE);
}
It's basically supposed to swap the style everytime you click the button. When i click the button, the button first gets selected by the OS (the border is becoming blue) and only after i click a second time on the exact same button it'll become green (the style that i'm giving it via setStyle).
However, the selected property becomes true on the first click and false on the second click, which means i click once on the button and it gets a blue border and selected = true, if i click on it a second time it becomes green and selected = false and if i click on it a third time it becomes normal again but selected will be true again.
I find it to be really strange that the first click on a button changes the "selected" variable correctly but not the style. Why is this happening and how can i avoid that i've to select the button first before i can click it?
You initialize
selected = false ;
and
setStyle(BASIC_STYLE);
But your event handler enforces the rule
selected == true -> setStyle(BASIC_STYLE);
selected == false -> setStyle(CLICKED_STYLE);
So your initial state is inconsistent with the state your handler enforces.
From the initial state, the first time you click, selected is set to true which causes setStyle(BASIC_STYLE) (which is the value it already has, so nothing changes). From then on, everything will switch as required.
You either need to change the initial state, or switch the logic of the setStyle(...) call in the handler.
public class ButtonEnterAction extends Button {
boolean selected = true;
public ButtonEnterAction(String connect) {
setText(connect);
action();
}
public ButtonEnterAction() {
action();
}
private void action() {
EventHandler<KeyEvent> enterEvent = (KeyEvent event) -> {
if (event.getCode() == KeyCode.ENTER) {
fire();
}
};
addEventFilter(KeyEvent.KEY_PRESSED, enterEvent);
// setOnMouseEntered(new EventHandler<MouseEvent>() {
// #Override
// public void handle(MouseEvent me) {
// SepiaTone st = new SepiaTone();
// setEffect(st);
// }
// });
// setOnMouseExited(new EventHandler<MouseEvent>() {
// #Override
// public void handle(MouseEvent me) {
// setEffect(null);
// }
// });
}
#Override
public void fire() {
super.fire(); //To change body of generated methods, choose Tools | Templates.
if (selected) {
SepiaTone st = new SepiaTone();
setEffect(st);
} else {
setEffect(null);
}
selected = !selected;
}
}
Create the Instant Class in ButtonEnterAction is like.
ButtonEnterAction bea = new ButtonEnterAction("TestButton");
bea.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("hello");
}
});

EventHandlers overriding each other? JavaFX

I have a context menu which activated on right click of a node in my JavaFX application. Originally I just had one menu item, but I decided to add another. When I added another, the second menu item worked but the first menu item stopped working. Here was my code:
final ContextMenu contextMenu = new ContextMenu();
MenuItem delete = new MenuItem("Delete");
MenuItem hyperlink = new MenuItem("Hyperlink...");
contextMenu.getItems().addAll(delete, hyperlink);
//handles deletion
webView.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
//handles hyperlink
webView.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
hyperlink.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
}
I assumed this was because the listeners were overriding each other, so I changed it to add event handlers. Here's how I changed the code:
final ContextMenu contextMenu = new ContextMenu();
MenuItem delete = new MenuItem("Delete");
MenuItem hyperlink = new MenuItem("Hyperlink...");
contextMenu.getItems().addAll(delete, hyperlink);
//handles deletion
webView.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
//handles hyperlink
webView.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
hyperlink.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
Now, NOTHING is happening when I right-click the node. Can someone explain why, and what my solution might be?
There is a number of things:
Yes, in your first version, the handlers were overriding each other.
Direct translation to addEventHandler style would use event type MouseEvent.MOUSE_PRESSED, not MOUSE_CLICKED, since MOUSE_CLICKED occurs only after the mouse button has been released, thus the isSecondaryButtonDown() test always returns false—that's why nothing is happening.
Instead of detecting mouse presses, you can listen to CONTEXT_MENU_REQUESTED events:
webView.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>() {
#Override
public void handle(ContextMenuEvent e) {
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
}
}
You should set the menu item's action right after its creation, not on each right click:
MenuItem delete = new MenuItem("Delete");
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
The only thing you need to do on right click is show the context menu, as shown above.
Why are you duplicating code. Just follow your first approach and do both things inside the same EventHandler.
One more note: Define your ActionEvents for the MenuItems outside of the webView handlers. What you are doing now is every time you click a menuItem you are resetting the ActionEvents. From your code, I can only see that you need to detect the MouseEvent just to show the Context Menu. Which can be done in one webView.setOnMousePressed(...).

What is instead of an action in JavaFX?

Suppose I want to have my program to react same way, say, navigate to next record, in response to different events, including pressing a key, clicking GUI button, selecting menu item and so on.
This was done with "actions" in Swing.
Can I materialize this concept in some program object in JavaFX?
Or I should make a porridge of interacting objects?
Action is still there in JavaFX. Example belows how to create an action, bind it to a keyboard shortcut and share between two different elements.
Button go = new Button("Go");
EventHandler<ActionEvent> goAction = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
browser.load(location.getText(), new Runnable() {
#Override
public void run() {
System.out.println("---------------");
System.out.println(browser.getHTML());
}
});
}
};
...
MenuItem menuItem = new MenuItem("Go!");
menuItem.setAccelerator(new KeyCodeCombination(KeyCode.G, KeyCombination.CONTROL_DOWN));
go.setOnAction(goAction);
menuItem.setOnAction(goAction);
JavaFX provides many events. You also do this with setOn() method:
button.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
// code here
}
});

Making a component less sensitive to Dragging in Swing

A JComponent of mine is firing a mouseDragged event too vigorously. When the user is trying to click, it interprets is as a drag even if the mouse has only moved 1 pixel.
How would I add a rule for a particular component that amounted to:
Do not consider it a drag event unless
the mouse has moved 10 pixels from the
point at which is was pressed down.
Note: I know it's not a system setting in my OS, since only events on that component suffer from this over sensitivity.
Thank you.
Previous answers combined together, with proper event type:
public class DragInsensitiveMouseClickListener implements MouseInputListener {
protected static final int MAX_CLICK_DISTANCE = 15;
private final MouseInputListener target;
public MouseEvent pressed;
public DragInsensitiveMouseClickListener(MouseInputListener target) {
this.target = target;
}
#Override
public final void mousePressed(MouseEvent e) {
pressed = e;
target.mousePressed(e);
}
private int getDragDistance(MouseEvent e) {
int distance = 0;
distance += Math.abs(pressed.getXOnScreen() - e.getXOnScreen());
distance += Math.abs(pressed.getYOnScreen() - e.getYOnScreen());
return distance;
}
#Override
public final void mouseReleased(MouseEvent e) {
target.mouseReleased(e);
if (pressed != null) {
if (getDragDistance(e) < MAX_CLICK_DISTANCE) {
MouseEvent clickEvent = new MouseEvent((Component) pressed.getSource(),
MouseEvent.MOUSE_CLICKED, e.getWhen(), pressed.getModifiers(),
pressed.getX(), pressed.getY(), pressed.getXOnScreen(), pressed.getYOnScreen(),
pressed.getClickCount(), pressed.isPopupTrigger(), pressed.getButton());
target.mouseClicked(clickEvent);
}
pressed = null;
}
}
#Override
public void mouseClicked(MouseEvent e) {
//do nothing, handled by pressed/released handlers
}
#Override
public void mouseEntered(MouseEvent e) {
target.mouseEntered(e);
}
#Override
public void mouseExited(MouseEvent e) {
target.mouseExited(e);
}
#Override
public void mouseDragged(MouseEvent e) {
if (pressed != null) {
if (getDragDistance(e) < MAX_CLICK_DISTANCE) return; //do not trigger drag yet (distance is in "click" perimeter
pressed = null;
}
target.mouseDragged(e);
}
#Override
public void mouseMoved(MouseEvent e) {
target.mouseMoved(e);
}
}
I've had to do exactly this before. Here's my mouse event processing code, cut down to just the bits relating to making drag require a few pixels before being treated as a drag.
public void mousePressed(int mod, Point loc) {
pressLocation=copyLocation(loc,pressLocation);
dragLocation=null;
}
public void mouseReleased(int mod, Point loc) {
if(pressLocation!=null && dragLocation!=null) {
// Mouse drag reverted to mouse click - not dragged far enough
// action for click
pressLocation=null;
}
else if(dragLocation!=null) {
// action for drag completed
}
else {
// do nothing
}
pressLocation=null;
dragLocation=null;
}
public void mouseDragged(int mod, Point loc) {
if(pressLocation!=null) { // initial drag actions following mouse press
dragLocation=pressLocation; // consider dragging to be from start point
if(Math.abs(loc.x-pressLocation.x)<dragMinimum && Math.abs(loc.y-pressLocation.y)<dragMinimum) {
return; // not dragged far enough to count as drag (yet)
}
// action drag from press location
pressLocation=null;
}
else {
// action drag from last drag location
dragLocation=copyLocation(loc,dragLocation);
}
}
And note, I also had problems with Java some JVM's generating click events after dragging, which I had to detect and suppress.
If I read your question correctly, you're tracking both click and mousedrag events. Can you track the coordinates upon mousedown, followed by a short computation in mousedrag to see if the mouse has moved your desired minimum numbers of pixels? Of course, you then also want to cancel/reset on mouseup or when the mouse is dragged outside the bounds of your JComponent.
Caveat: I haven't done this myself, but I think it's where I'd start if it were me.
Software Monkey's code seemed to be missing some code, so I wrote this solution:
navigationTree.addMouseListener(new DragInsensitiveMouseClickListener(10) {
#Override
public void mouseClicked(MouseEvent e) {
TreePath treePath = navigationTree.getPathForLocation(e.getX(), e.getY());
if(treePath != null) {
processChoice();
}
}
});
This will still fire the mouseClicked() event when the user produces at most 10 pixels of "drag travel".
The code for the click listener:
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class DragInsensitiveMouseClickListener extends MouseAdapter {
private final int allowedTravel;
public Point mouseDownPoint;
public DragInsensitiveMouseClickListener(int allowedTravel) {
this.allowedTravel = allowedTravel;
}
#Override
public void mousePressed(MouseEvent e) {
mouseDownPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
double horizontalTravel = Math.abs(mouseDownPoint.getX() - e.getX());
double verticalTravel = Math.abs(mouseDownPoint.getY() - e.getY());
if (horizontalTravel < allowedTravel && verticalTravel < allowedTravel) {
mouseClicked(e);
}
}
}

Categories

Resources