mouseEntered can't catch fast mouse movement - java

I have an array of JToggleButtons that make a calendar selector of sorts. I have implemented a way to drag the mouse over the buttons to toggle multiple days without having to stop and click each one, and it works great for slower mouse movements:
JToggleButton[] buttons = getCalendarDayButtonArray(); //arbitrary instantiation
for (int d = 0; d < 31; d++) {
final JToggleButton b = new JToggleButton(day);
buttons[d] = b;
buttons[d].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {//do stuff...}
}
buttons[d].addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent e) {
if (e.getModifiers() == MouseEvent.BUTTON1_MASK) {
b.doClick();
}
}
public void mousePressed(MouseEvent e) {
if (e.getModifiers() == MouseEvent.BUTTON1_MASK) {
b.doClick();
}
}
});
}
However, this doesn't work entirely for quick movements. I am not sure whether it is a problem with the polling rate of the mouse, or a result of any lag on the computer itself, but it seems that the mouse skips over some buttons entirely and as a result the mouseEntered method is not invoked for these buttons. Is there a workaround that doesn't involve the user simply moving the mouse slowly? Thanks in advance!

Related

Swing JSplitPane - Is there a way to listen to a click on the divider's arrows?

I'm trying to catch the event when the small "minimize" or "maximize" arrows of a JSplitPane's divider are clicked.
I found a way to listen to a click elsewhere on the divider bar. Something like :
SplitPaneUI spui = splitPane.getUI();
if (spui instanceof BasicSplitPaneUI) {
((BasicSplitPaneUI) spui).getDivider().addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
// do something...
}
});
}
But is there a way to listen to clicks on the arrows?
I'm trying to catch the event when the small "minimize" or "maximize" arrows of a JSplitPane's divider are clicked.
Maybe you could add a PropertyChangeListener to the JSPlitPane and listen for dividerLocation changes (assuming you don't care if the divider was dragged or "one clicked" to the start or end). Then you can check if the divider location is at 0 or the maximum.
splitPane.addPropertyChangeListener("dividerLocation", this);
...
public void propertyChange(PropertyChangeEvent e)
{
// Get the new divider location of the split pane
int location = ((Integer)e.getNewValue()).intValue();
if (location == 0)
// do something
else if (location == splitPane.getMaximumDividerLocation())
// do something else
}
If you do care about the difference between dragging and clicking, then maybe you can compare the old/new values and look for a change in the location greater than a specified value.
Another option is to get the button from the divider:
JSplitPane splitPane = (JSplitPane)e.getSource();
BasicSplitPaneUI ui = (BasicSplitPaneUI)splitPane.getUI();
BasicSplitPaneDivider divider = ui.getDivider();
JButton button1 = (JButton)divider.getComponent(0)
Now you can add another ActionListener to the button to do your custom code.
Reflection is the Only way I guess to listen to One Touch Expandable Buttons
Just pass addSplitPaneListener and it is done.
public void addSplitPaneListener(JSplitPane splitPane) {
addSplitPaneListener("leftButton", splitPane);
addSplitPaneListener("rightButton", splitPane);
}
public void addSplitPaneListener(String button, JSplitPane splitPane) {
try {
Field field = BasicSplitPaneDivider.class.getDeclaredField(button);
field.setAccessible(true);
JButton onetouchButton = (JButton) field.get(((BasicSplitPaneUI) splitPane.getUI()).getDivider());
onetouchButton.setActionCommand(button);
onetouchButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
buttonClicked(e);
}
});
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
//Logger.getLogger(NewJFrame1.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void buttonClicked(ActionEvent e) {
System.out.println("Clicked " + e.getActionCommand());
}
Here is a derived class that deals with expanding/hiding either using the one touch expander and/or an additional keylistener bound to a certain key to hide/unhide the RIGHT panel. Doing it for the left should easy to adapt ;)
Interestingly on Linux the right part will never get its theoretical maximum value but will be one less (BUGCLICKONETOUCH). Didn't test if this is also true on Windows.
import java.awt.Component;
import javax.swing.JSplitPane;
public class JToggleSplitPane extends JSplitPane {
private int restoreWidth = -1;
private final static int BUGCLICKONETOUCH = 1;
public JToggleSplitPane(int splittype, Component c1, Component c2) {
super(splittype, c1, c2);
setOneTouchExpandable(true);
}
public void toggle() {
try {
if (getDividerLocation() < getWidth() - getDividerSize() - BUGCLICKONETOUCH) {
restoreWidth = getWidth() - getDividerSize() - getDividerLocation();
setDividerLocation(1.0d);
} else {
setDividerLocation(getWidth() - getDividerSize() - restoreWidth);
}
updateUI();
doLayout();
} catch (Exception e) {
e.printStackTrace();
}
}
public void doLayout() {
super.doLayout();
if (restoreWidth == -1) {
restoreWidth = rightComponent.getWidth();
}
}
}

With JLayeredPane.DRAG_LAYER activated, only mouse pressed gets executed--not dragged and released

I'm new to JLayeredPane, so I assume that's partly to blame for why I can't understand
WHY the mouse pressed/dragged/released methods do not execute as they should when I drag
a txtUser item (which has listeners attached; see bottom).
With the given code segments. Only userMousePressed executes when I drag.
HOWEVER, IF the line layeredPane.add(userDragLetter, LayeredPane.DRAG_LAYER) is commented out, all 3 mouse events are recognized and executed properly.
private void userMousePressed(MouseEvent e){
curUserCell.index = interpIndex(e.getXOnScreen(),ulcUser.x,txtUser[0].getWidth());
curUserCell.background = txtUser[curUserCell.index].getBackground();
curUserCell.content = txtUser[curUserCell.index].getText().charAt(0);
txtUser[curUserCell.index].setBackground(B_DRAGGING);
txtUser[curUserCell.index].setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
int w = Integer.parseInt(e.getComponent().getName());
userDragLetter = null;
Component c = txtUser[w];
parentLocation = c.getParent().getLocation();
xAdjustment = parentLocation.x - e.getX();
yAdjustment = parentLocation.y - e.getY()- gamePanel.getHeight();
userDragLetter = (JLabel)c;
userDragLetter.setPreferredSize(new Dimension(CELLSIZE, CELLSIZE));
userDragLetter.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment);
//////////////////////////////////////////////////////////////////////////////////////
// If I comment out the line below, `userMouseDragged` & `userMouseReleased` DO execute
layeredPane.add(userDragLetter, JLayeredPane.DRAG_LAYER);
/////////////////////////////////////////////////////////////////////////////////////
}
This method doesn't get executed with code above 'as is':
private void userMouseDragged(MouseEvent me) {
System.out.print("User mouse dragged entered..."); // Doesn't print
...
}
This method doesn't get executed either:
private void userMouseReleased(MouseEvent e)
{
System.out.println("User mouse released entered..."); // Doesn't print
...
}
Here's how I assign the mouse listeners:
txtUser[k].addMouseMotionListener
(
new MouseMotionAdapter()
{
public void mouseDragged(MouseEvent evt)
{
userMouseDragged(evt);
}
}
);
txtUser[k].addMouseListener
(
new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
userMousePressed(e);
}
public void mouseReleased(MouseEvent e)
{ //after dragging user letter
userMouseReleased(e);
}
}
);
(What I'm trying to do is to physically drag--i.e., have mouse pointer literally point at--a letter from txtUser[...] as it's dragged to a game grid. I'm not close, but as is it's broken! If I comment out the layeredPane line, program works as always.)
(a) Why do mouse dragged and mouse released events get ignored?
(b) What should I do to make dragged and released events trigger their methods?

Simulate MousePress to enter MouseDragged

I am trying to achieve:
1. User performs a mouse-press on parent JFrame
2. Child JFrame becomes visible at mouse location
3. While mouse-button remains pressed, the user can drag the child JFrame across the screen by moving the mouse
The problem:
I can mimic a mouse-press but it does not 'grab' the child JFrame--hence the child JFrame is not being dragged unless the user manually clicks the child JFrame again. I want the process to be smooth without any interruptions: i.e. steps 1-3 (above) should all execute with a single mouse-press.
Failed attempts:
1. I have tried using Robot's mousePressed() to simulate an additional mouse-press on the child. This works, however, it's not clean and can be quite buggy--especially if the PC/device is slow or the user moves the mouse too quickly. This is not a good solution.
2. Using the Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(new MouseEvent()); results in the exact same issue as depicted by the current code.
3. When adding a KeyListener (for testing) to both the child and the parent, when the mouse-press is made, the child window is focused and responds to the implemented KeyListener--the parent's KeyListener is not activated..
The Code:
final JFrame parent = new JFrame(), child = new JFrame();
parent.setSize(256, 256);
child.setSize(128,128);
parent.setVisible(true);
parent.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
child.setVisible(true);
child.setLocation(e.getXOnScreen()-48, e.getYOnScreen()-48);
int id = MouseEvent.MOUSE_PRESSED;
long time = System.currentTimeMillis();
int x = 48;
int y = 48;
int button = MouseEvent.BUTTON1_MASK;
child.dispatchEvent(new MouseEvent(child, id, time, button, x, y, 1, false));
}
});
child.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
child.setLocation(e.getXOnScreen()-48, e.getYOnScreen()-48);
}
});
child.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Pressed!");
}
});
Even though the child JFrame has the focus, the MousePress remains active on the parent. To resolve the issue in a stable manner, the parent should 'forward' its MouseDragged event to the child as shown below:
parent.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
child.setVisible(true);
child.setLocation(e.getXOnScreen()-48, e.getYOnScreen()-48);
}
});
parent.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
child.dispatchEvent(new MouseEvent(child, e.getID(), e.getWhen(), e.getButton(), e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()));
}
});
child.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
child.setLocation(parent.getX()+e.getX()-48, parent.getY()+e.getY()-48);
}
});
Even this is not fully 'clean' because the code is relying on the parent's frame visibility. However, it is a stable solution (unlike Robot).

Emulate touch scroll in Java

I am searching for a way to create a touch application in Java (NOT for mobile devices) with a touch scroll support.
Ive been searching so far and I am investigating how this could be done - what ive found is the MT4J (http://www.mt4j.org/) but it seems that it would not support that (please correct me if i am wrong).
So my question is, how can i emulate a scroll event on a horizontal touch / swipe?
Thanks for help!
Kind Regards,
Alex
This implements a touch and drag scroller by sub-classing JScrollPane.
Because touch and drag is not enough on its own I have added momentum
so that it 'throws' the scroll when the mouse button is released.
There is no 'bounce' at the ends of the scroll as I cannot afford to
fight a lawsuit against the owners of 'bounce'.
It's not completely encapsulated because, although it works fine if
the view is a jlist, there may be components on the view that need
to modify their response if the panel was being dragged at the time.
Also some components, e.g. JRadioButton, JCheckBox, etc, consume
mouse clicks without passing them up to the container, so you will
need to add TouchScroll's MouseListener and MouseMotionListener to them.
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
public class TouchScroll extends JScrollPane implements MouseListener, MouseMotionListener {
private JScrollBar vbar = this.getVerticalScrollBar();
public TouchScroll(Component view){ // 1-arity CONSTRUCTOR
super(view);
view.addMouseListener(this);
view.addMouseMotionListener(this);
}
public TouchScroll() { super(); } // 0-arity CONSTRUCTOR
public void setViewportView(Component view) {
super.setViewportView(view);
view.addMouseListener(this);
view.addMouseMotionListener(this);
}
private static boolean wasdragged = false; // other MouseListeners may need to know this ...
public boolean wasDragged() { return wasdragged; } // ... this gives them safe access
static int lastY = 0, distY = 0;
double momentum = 0; // not really physical momentum but it will be used to 'throw' the scroll when the mouse button is released
static boolean lbdown = false;
#Override
public void mouseDragged(MouseEvent e) {
wasdragged = true;
distY = 0;
int currentY = e.getYOnScreen();
if(lbdown) {
distY = lastY - currentY;
vbar.setValue(distY + vbar.getValue());
if(Math.abs(distY) > 1)
momentum = distY + distY; // magnify and log the momentum for later use
}
lastY = currentY;
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
lastY = e.getYOnScreen();
lbdown = true;
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1)
lbdown = false;
if(wasdragged)
wasdragged = false;
if(Math.abs(momentum) <= 4.0) // then assume that the mouse wasn't moving (much) when the button was released
return;
// otherwise 'throw' the scroll
int max = vbar.getMaximum();
int count;
double brakingforce = 1.04; // set an initial braking force
for(count = 1000; count > 0; count--){ // don't allow it to accidentally go on forever
momentum = momentum / brakingforce; // apply the brake
if(Math.abs(momentum) < 1.5)
brakingforce = 1.02; // ease off the brake towards the end (gives a slight overrun ala iOS)
if(Math.abs(momentum) < 1.0) // bring it to a halt
break;
int val = vbar.getValue();
if(val < 0 || val > max) // prevent overrun
break;
vbar.setValue((int) momentum + val); // increment the scroll bar
try {
Thread.sleep(10); // slow the loop down so the user can see the scroll
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
public void mouseMoved(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
Here is a 'bare bones' example of how you might apply it:
panel = new JPanel();
jlist = new JList(list);
scroller = new TouchScroll(jlist);
panel.add(scroller);
Any components that respond to a mouse release may need to do something like this:
public void mouseReleased(MouseEvent e) {
if(scroller.wasDragged())
return;
actionENTER();
}
Lastly, as mentioned earlier, components that consume mouse events will need
to inform the scroller like this:
JCheckBox checkbox = new JCheckBox(text);
checkbox.addMouseMotionListener(scroller);
checkbox.addMouseListener(scroller);
You can use JavaFX's touch event framework.
Here is some sample code from the linked tutorial on handling scroll events:
rect.setOnScroll(new EventHandler<ScrollEvent>() {
#Override public void handle(ScrollEvent event) {
if (!event.isInertia()) {
rect.setTranslateX(rect.getTranslateX() + event.getDeltaX());
rect.setTranslateY(rect.getTranslateY() + event.getDeltaY());
}
log("Rectangle: Scroll event" +
", inertia: " + event.isInertia() +
", direct: " + event.isDirect());
event.consume();
}
});
rect.setOnScrollStarted(new EventHandler<ScrollEvent>() {
#Override public void handle(ScrollEvent event) {
inc(rect);
log("Rectangle: Scroll started event");
event.consume();
}
});
rect.setOnScrollFinished(new EventHandler<ScrollEvent>() {
#Override public void handle(ScrollEvent event) {
dec(rect);
log("Rectangle: Scroll finished event");
event.consume();
}
});

MouseListener in Java Swing sometimes not respond

I've implemented right mouse click for open menu listener on my main Jframe, it works fine except one problem. One out of 5 (give or take) clicks it not responding, this can be very annoying for the user. Here is my code:
contentPane = new JPanel();
contentPane.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3)
{
//Do Stuff
}
}
});
Can you please help me
You won't get clicks from sub-components of contentPane.
I think your problem is that you have added things to your panel. When the user clicks at regions occupied by a sub-component, that sub-component get's the click event.
Quick fix: I would recommend you to add the same mouse listener to all sub-components.
You are not "clicking"
A click is when the mouse is pressed and release really quickly. If you are not careful you might get events for (for instance) "pressed, moved, released" instead of "clicked".
Quick fix: use mouseReleased event instead.
Use this Code instead:
private MouseAdapter listener = new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
if (downer) {
downer = false;
if (new Rectangle(e.getComponent().getLocationOnScreen(), e.getComponent().getSize())
.contains(e.getLocationOnScreen())) {
downer = false;
// CODE
new Thread(new Runnable(){
public void run(){
//Your Listener code
}
}).start();
/// COde
}
}
}
boolean downer = false;
public void mousePressed(java.awt.event.MouseEvent e) {
downer = true;
}
};
This code only reacts if you press on the component and release on the component AND starts a new Thread for the custom task. This should work allways, because the AWT Thread isnt blocked with long calculations.

Categories