Passing mouse events from Jframe glass to menu bar? - java

Based on previous stack questions I have written the following code to capture mouse input from the glass pane push it to a series of panels underneath.
MouseMotionListener mml = new MouseMotionListener() {
private void DispatchMouseEvent(MouseEvent e) {
Point glassPanePoint = e.getPoint();
Container container = jf.getContentPane();
Point containerPoint = SwingUtilities.convertPoint(
glass, glassPanePoint, container);
if (containerPoint.y >= 0) {
for (Component ml : mouseListeners) {
Point componentPoint = SwingUtilities
.convertPoint( glass, glassPanePoint, ml);
ml.dispatchEvent(new MouseEvent(ml, e.getID(),
e.getWhen(), e.getModifiers(),
componentPoint.x, componentPoint.y,
e.getClickCount(), e.isPopupTrigger()));
}
}
}
#Override
public void mouseDragged(MouseEvent me) {
DispatchMouseEvent(me);
}
#Override
public void mouseMoved(MouseEvent me) {
DispatchMouseEvent(me);
}
};
glass.addMouseMotionListener(mml);
But all of the tutorials I can find say something like this when it comes to the menu bar.
if (containerPoint.y < 0) { //we're not in the content pane
if (containerPoint.y + menuBar.getHeight() >= 0) {
//The mouse event is over the menu bar.
//Could handle specially.
} else {
//The mouse event is over non-system window
//decorations, such as the ones provided by
//the Java look and feel.
//Could handle specially.
}
Could someone point me to somewhere so I can see how to handle that specifically? Thanks.

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?

Swing popup menu on a component and its content

I have a component (Widget - extends a JPanel) on which I have implemented a simple popup menu. It works when clicking on the border of the panel and basically everywhere except for the places where the panel layout contains some other component inside the panel.
So if there is a JTable inside the panel, I can invoke the menu when clicking next to it (if there is nothing else) but when clicking on the JTable, nothing happens (the table is obviously on top of the panel preventing the MouseAdapter to register the click).
Can I somehow make the popup menu invoked when right clicking even on the components inside the panel? Here is the sample code how I create and invoke the menu:
private void initPopupMenu() {
popup = new JPopupMenu();
JMenuItem closeItem = new JMenuItem("Close");
closeItem.setActionCommand(WidgetConstants.Actions.CLOSE.name());
closeItem.addActionListener(this);
popup.add(closeItem);
JMenuItem minimizeItem = new JMenuItem("Minimize");
minimizeItem.setActionCommand(WidgetConstants.Actions.MINIMIZE.name());
minimizeItem.addActionListener(this);
popup.add(minimizeItem);
}
MouseInputListener componentListener = new MouseInputAdapter() {
#Override
public void mousePressed(MouseEvent me) {
// popup
if (me.isPopupTrigger()) {
popup.show(me.getComponent(), me.getX(), me.getY());
}
}
#Override
public void mouseReleased(MouseEvent ev) {
if (ev.isPopupTrigger()) {
popup.show(ev.getComponent(), ev.getX(), ev.getY());
}
}
}
#Override
public void setBorder(Border border) {
removeMouseListener(componentListener);
removeMouseMotionListener(componentListener);
if (border instanceof WidgetBorder) {
addMouseListener(componentListener);
addMouseMotionListener(componentListener);
}
super.setBorder(border);
}
Thank you for any tips.
First of all: you don't need to use a mouse listener. Each JComponent has the method setComponentPopupMenu(JPopupMenu). Secon: you can traverse the component tree an register the popup menu for each component.
Here is example:
/**
* Provides component hierarchy traversal.
*
* #param aContainer start node for the traversal.
*/
private void traverse(Container aContainer, JPopupMenu aMenu) {
for (final Component comp : aContainer.getComponents()) {
if (comp instanceof JComponent) {
((JComponent) comp).setComponentPopupMenu(aMenu);
}
if (comp instanceof Container) {
traverse((Container) comp, aMenu);
}
}
}

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();
}
});

swing mouse listeners being intercepted by child components

I have a swing component that has several sub components. What I want to do change some label if the mouse is over any of those components, and then change it to something else if the mouse moves off all of the components. I'm trying to find a more efficient way to do this.
Currently I have mouse listeners over all of the child components that look something like:
class AMouseListener extends MouseAdapter {
private boolean mouseOver;
mouseEntered(MouseEvent e) { mouseOver = true; updateLabel(); }
mouseExited(MouseEvent e) { mouseOver = false; updateLabel(); }
void updateLabel() {
String text = "not-over-any-components";
// listeners are each of the listeners added to the child components
for ( AMouseListener listener :listeners ) {
if ( listener.mouseOver ) {
text = "over-a-component";
break;
}
}
}
}
This works, but I feel like there should be a better way to handle this by only handling mouseEntered and mouseExited events on the parent container, but since the child components intercept these events, I'm not sure how to go about doing this (I don't necessarily have control over the child components so I Can't forward the mouse events to the parent event if I wanted to).
For example
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class TestMouseListener {
public static void main(String[] args) {
final JComboBox combo = new JComboBox();
combo.setEditable(true);
for (int i = 0; i < 10; i++) {
combo.addItem(i);
}
final JLabel tip = new JLabel();
tip.setPreferredSize(new Dimension(300, 20));
JPanel panel = new JPanel();
panel.add(combo);
panel.add(tip);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setVisible(true);
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
tip.setText("Outside combobox");
}
#Override
public void mouseExited(MouseEvent e) {
Component c = SwingUtilities.getDeepestComponentAt(
e.getComponent(), e.getX(), e.getY());
// doesn't work if you move your mouse into the combobox popup
tip.setText(c != null && SwingUtilities.isDescendingFrom(
c, combo) ? "Inside combo box" : "Outside combobox");
}
});
}
private TestMouseListener() {
}
}
Check out the docs and examples for the "glass pane".
This should give you what you need: The Glass Pane
I know this is very old, but here's a simple solution with which you can create a mouse listener for a component and all components inside it's bounds (without adding the listener to all components individually):
/**
* Creates an {#link AWTEventListener} that will call the given listener if
* the {#link MouseEvent} occurred inside the given component, one of its
* children or the children's children etc. (recursive).
*
* #param component
* the component the {#link MouseEvent} has to occur inside
* #param listener
* the listener to be called if that is the case
*/
public static void addRecursiveMouseListener(final Component component, final MouseListener listener) {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
if(event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
if(mouseEvent.getComponent().isShowing() && component.isShowing()){
if (containsScreenLocation(component, mouseEvent.getLocationOnScreen())) {
if(event.getID() == MouseEvent.MOUSE_PRESSED) {
listener.mousePressed(mouseEvent);
}
if(event.getID() == MouseEvent.MOUSE_RELEASED) {
listener.mouseReleased(mouseEvent);
}
if(event.getID() == MouseEvent.MOUSE_ENTERED) {
listener.mouseEntered(mouseEvent);
}
if(event.getID() == MouseEvent.MOUSE_EXITED) {
listener.mouseExited(mouseEvent);
}
if(event.getID() == MouseEvent.MOUSE_CLICKED){
listener.mouseClicked(mouseEvent);
}
}
}
}
}
}, AWTEvent.MOUSE_EVENT_MASK);
}
/**
* Checks if the given location (relative to the screen) is inside the given component
* #param component the component to check with
* #param screenLocation the location, relative to the screen
* #return true if it is inside the component, false otherwise
*/
public static boolean containsScreenLocation(Component component, Point screenLocation){
Point compLocation = component.getLocationOnScreen();
Dimension compSize = component.getSize();
int relativeX = screenLocation.x - compLocation.x;
int relativeY = screenLocation.y - compLocation.y;
return (relativeX >= 0 && relativeX < compSize.width && relativeY >= 0 && relativeY < compSize.height);
}
Note: Once the mouse exits the root component of this listener the mouseExited(mouseEvent) will probably not fire, however you can just add the mouse listener to the root component itself and it should fire.
mouseExited(mouseEvent) is unreliable in general though.
You could initiate a single instance of the listener and add that instance to each component.
Like this:
AMouseListener aMouseListener=new AMouseListener();
for each(Component c:components) {
caddMouseListener(aMouseListener);
}

Categories