Allow parent to handle mouse event - java

I added a JPanel to a JRadioButton - so that I may display whatever I want in the radio button.
This all worked as expected. But to allow for text wrapping, I used a JTextArea and added it to the JPanel contained within the radio button.
Now I have an issue where, if the user clicks on the JTextArea, then the JTextArea consumes the mouseEvent and as a result there is no response from the radio button (it doesn't 'select').
Is there a way get the JTextArea to ignore the mouse click, so that the parent may handle it instead?
I tried add the JTextArea's listeners to the radioButton instead.
I also tried to remove its listeners completely, but both these attempts failed.
Anyone have any suggestions?

Strong beware
Most JSomething are not meant to be used as containers even though it's possible - the outcome of doing it anyway is more or less visually and behaviourally undetermined!
That said, did it recently, to implement something similar to a Windows task dialog. If the requirement includes keeping the button clickable (and why else would you mis-use it as a container :-) the main problem (layout apart) is to make all added components completely mouse-transparent. Which is more difficult than can be expected. The minimum is to not allow adding of mouseListeners and disable the acceptance of mouseEvents:
final JTextArea area = new JTextArea("replacement ..") {
#Override
public synchronized void addMouseListener(MouseListener l) {
LOG.info("adding here ...?");
}
#Override
public synchronized void addMouseMotionListener(
MouseMotionListener l) {
}
#Override
public synchronized void addMouseWheelListener(
MouseWheelListener l) {
}
#Override
public void addNotify() {
disableEvents(AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK |
AWTEvent.MOUSE_WHEEL_EVENT_MASK);
super.addNotify();
}
};
Plus make sure it's not focusable
area.setEditable(false);
area.setFocusable(false);
area.setRequestFocusEnabled(false);
Plus unregister dragging and tooltips
ToolTipManager.sharedInstance().unregisterComponent(area);
area.setDragEnabled(false);
Nevertheless, there might still be surprises ahead, f.i. call the following twice (that is disable and enable again), which will internally re-enable mouseEvent:
area.setAutoscrolls(!area.getAutoscrolls());
So at the end of the day, we might get away with it - but never be entirely certain that we succeeded.

What about this? Create and add your own MouseListener to TextArea
JPanel p = new JPanel();
JTextArea t = new JTextArea("line \n line");
t.addMouseListener(new MyMouseListener());
p.add(t);
jRadioButton1.add(p);
jRadioButton1.addMouseListener(new MyRadioButtonMouseListener());
And in the MyMouseListener Dispatch event
private class MyMouseListener implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
Component source = (Component) e.getSource();
source.getParent().getParent().dispatchEvent(e); // 2x getParent() because JTextArea->JPanel->JRadio
}
.
.
.
}
And finally RadioButtonMouseListener
private class MyRadioButtonMouseListener implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("CLICK ON RADIOBUTTON !!");
}
.
.
.
}

Related

Adapter pattern with Buttons, adaptor class has to know which button was pressed

This is an actionPerformed in a Swing panel with custom buttons from a framework which scrambles their classes so all methods are a():String or b():void and there is no way to make out what it actually is.
I got a compiler error becaus when I inherit this button class the compiler find a():void an a():String which is not allowed in Java. My solution was to use the adapter pattern like this:
public abstract class FactoryButton {
private CustomButton button;
public FactoryButton(int width, int height) {
button = new DynButton();
button.setSize(width, height);
}
public DynButton getButton() {
return button;
}
}
So my FactoryButton has the CustomButton class as a private member. The FactoryButton is the parent of another Button class named FactorySelectionButton
which has an action performed where I used to be able to get the source of the event:
#Override
public void actionPerformed(ActionEvent arg0) {
if (arg0.getSource() instanceof FactorySelectionButton) {
// User selected a factory
selectedItem = ((FactorySelectionButton) arg0.getSource()).getFactory();
// Close the screen, so control returns back to the parent window
cancel();
} else {
// other buttons implementation
}
}
But now since I solved one problem with the adapter pattern I have another the arg0.getSource() no longer gives me the FactorySelectionButton but it now gives a CustomButton which gives me no way to know which custom button is pressed.
The reason for not throwing away the custom button is that I am bound to the framework, I have to use it and the amount of factories can grow so I don't want hardcoded buttons.
So anyone have an idea on how I can fix this?
I found a way around it by looping over all my components and checking whether they have the button I need and they double checking whether it's really an instance of the class I want.
#Override
public void actionPerformed(ActionEvent arg0) {
for (FactoryButton component : components) {
if(component.getButton().equals(arg0.getSource()) && component instanceof FactorySelectionButton)
selectedItem = ((FactorySelectionButton) component).getFactory();
return;
}
//other buttons implementation
}

Two different buttons in one Panel in my own Component

I'm trying to create JPanel with two different buttons which one of them increasing and second decreasing size of text or window. I have class with button declaration. Everything is working when I put these buttons on JFrame separately.
I don't know how to get Action Listener in JPanel of each buttons. All I possibly do is listener of mouse click on JPanel...
Could you help me? I'm really begginer with coding so be polite please :]
public class ButtonMy extends Component {
private ButtonIncrease increase;
private PropertyChangeSupport propertyChangeSupport;
public ButtonMy() {
setPreferredSize(new Dimension(30,30));
kolor = Color.blue;
setForeground(kolor);
propertyChangeSupport = new PropertyChangeSupport(this);
increase = ButtonIncrease.Powieksz;
}
public ButtonIncrease getIncrease() {
return increase;
}
public void setIncrease(ButtonIncrease increase) {
ButtonIncrease oldIncrease = this.increase;
this.increase = increase;
propertyChangeSupport.firePropertyChange("increase", oldIncrease, increase);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
propertyChangeSupport.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
propertyChangeSupport.removePropertyChangeListener(l);
}
}
There is JPanel for bind 2 buttons. Here is the biggest problem :/ I'm lack of ideas.
public class ButtonB extends JPanel implements ActionListener{
public ButtonMy b1 = new ButtonMy();
public ButtonMy b2 = new ButtonMy();
public ButtonB (){
init();
}
public final void init(){
setLayout(new GridLayout(1,2));
this.przycisk1.setIncrease(ButtonIncrease.Powieksz);
this.przycisk2.setIncrease(ButtonIncrease.Zmniejsz);
add(b1);
add(b2);
}
}
JFrame where I test this component is very common. Code below shows only function for inc and dec size when separate button is clicked (not in JPanel).
private void buttonMy3MouseClicked(java.awt.event.MouseEvent evt) {
switch(buttonMy3.getIncrease()) {
case Powieksz: setSize(1);
break;
case Zmniejsz: setSize(0);
break;
}
}
I didn't paste full of my code. There some of math functions left which I think they are not needed here (setSize for example).
I'm not sure if i understand the problem correctly but I think under the actionListener class you should have a method called actionPerformed& it will say that if button1 is clicked increase the number, if button2 is clicked decrease the number:
public void actionPerformed( ActionEvent event ) {
if (event.getSource()== b1) // your "increase size" code
if(event.getSource()== b2)// your "decrease size" code
}
button listeners are actually different from mouse listeners; buttons implements ActionListeners and have the actionPerformed method with event variable. you could handle the event by:
getSource() -this method is inherited from java.util.EventObject and returns the OBJECT on which the event initially occurred (the button itself)
or by getActionCommand() -this method is available to action events, or any event that inherits from ActionEvent and returns the command STRING associated with this action.
however mouse listeners implements MouseListener and has a lot of methods depending on what the mouse does (pressed, clicked, released, etc.).

JList MouseMoved and MousePressed

I've extended a JList to provide two separate functionalities, toolTipText for items, and right-click options. Both work separately, but when I try to use them together, the MouseMoved events aren't being recognized? Below are the guts of my new listener methods. How should I be negotiating these various events?
public class JListTT extends javax.swing.JList {
public JListTT() {
super();
addMouseListener(new ttListener());
...
class ttListener extends MouseAdapter {
public void mouseMoved(MouseEvent e) {
String nodeID = bldItemNodeID();
theList.setToolTipText(nodeID);
}
public void mousePressed(MouseEvent ev) {check(ev); }
public void mouseReleased(MouseEvent ev) {check(ev); }
public void mouseClicked(MouseEvent ev) {check(ev); }
public void check(MouseEvent ev) {
if (ev.isPopupTrigger()) {
theList.setSelectedIndex(theList.locationToIndex(ev.getPoint()));
menu.show(theList, ev.getX(), ev.getY());
}
}
}
You add the ttListener object as a MouseListener, but I don't see you adding the ttListener object as a MouseMotionListener. For example:
ttListener myMouseadapter = new ttListener();
addMouseListener(myMouseadapter);
addMouseMotionListener(myMouseadapter);
I did not test this myself, but looking at the javadoc of JList the tooltip functionality is available out of the box. The javadoc of JList#getTooltipText clearly states
Overrides JComponent's getToolTipText method in order to allow the
renderer's tips to be used if it has text set.
So if your ListCellRenderer returns a Component in the getListCellRendererComponent method which has a tooltip it will be displayed by the JList without the need of a listener.
there's not necessarily a need for a low-level approach as a custom mouse-/motionListener:
as to a per-cell tooltip, see #Robin's answer
as to a context menu, JComonent has a property componentPopupMenu: using that will cope with opening the menu on keyboard short-cut automatically
"not necessarily" because you seem to rely on the cell being selected on right click. If so, you would still need a MouseListener to trigger the selection (after decade long debates, Swing doesn't - which seems to be unusual in current native apps ;-)
You can achieve it by using mouseDragged
YourClass extends JPanel implements MouseListener{
......
#Override
public void mouseDragged(MouseEvent e) {
//code go here
}
}

MouseEvent of JTabbedPane

I want to show a small popup menu when you right-click a tab, now this is working fine but when you right click it also selects that tab which is unwanted.
So my idea was to make a new class, extend JTabbedPane and recode those mouse events. Problem is that I have no idea where to start, I was browsing its source but I can't find what part is handeling the mouseEvents.
Tabs.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
if(me.getButton()==3){
int tabNr = ((TabbedPaneUI)Tabs.getUI()).tabForCoordinate(Tabs, me.getX(), me.getY());
Component clickedTab = EventsConfig.window.MainTabs.getComponentAt(tabNr);
newMenu(clickedTab, me.getX(), me.getY());
}
}
});
Beware: dirty hack ahead! The only reason I recommend it, is that I consider the behaviour (select on right press) a bug in the BasicTabbedPaneUI's Handler.
The basic idea is to grab the listener installed by the ui, remove it, wrap into a custom listener which delegates everything except a right pressed to the original and add that to the pane:
private void installMouseListenerWrapper(JComponent tabbedPane) {
MouseListener handler = findUIMouseListener(tabbedPane);
tabbedPane.removeMouseListener(handler);
tabbedPane.addMouseListener(new MouseListenerWrapper(handler));
}
private MouseListener findUIMouseListener(JComponent tabbedPane) {
MouseListener[] listeners = tabbedPane.getMouseListeners();
for (MouseListener l : listeners) {
if (l.getClass().getName().contains("$Handler")) {
return l;
}
}
return null;
}
public static class MouseListenerWrapper implements MouseListener {
private MouseListener delegate;
public MouseListenerWrapper(MouseListener delegate) {
this.delegate = delegate;
}
#Override
public void mouseClicked(MouseEvent e) {
delegate.mouseClicked(e);
}
#Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) return;
delegate.mousePressed(e);
}
#Override
public void mouseReleased(MouseEvent e) {
delegate.mouseReleased(e);
}
#Override
public void mouseEntered(MouseEvent e) {
delegate.mouseEntered(e);
}
#Override
public void mouseExited(MouseEvent e) {
delegate.mouseExited(e);
}
}
then you have to add JPopupMenu (or JToolTip on MouseHoverOver ) to the JTabbedPane
A possible workaround is to set your custom tab component for each tab - see JTabbedPane#setTabComponentAt(...). Add a mouse handler to your custom tab component and redispatch left click events to the tabbedPane as described at http://www.jyloo.com/news/?pubId=1315817317000.
The custom tab component can be a simple JLabel (used for the tab title) or a container for multiple components. Depending on your requirements you can e.g. add an arrow button which will open a popup menu by left clicking the related button.
This article will helpful for removing unwanted tab selection when you click right mouse button.
Stop right click Event on JTabbedPane
I liked to add more about removing Mouse Listeners.
Try to override the method rather than removing it. It's better for future code updates.
The problem is BasicTabbedPaneUI's have inner class called Handler. That handler class override Mouse Listener.
To stop right click tab selection and show pop up menu; we need to override this method in BasicTabbedPaneUI,
protected MouseListener createMouseListener() {
return getHandler();
}
To get better look and feel we should override SynthTabbedPaneUI class.
SynthTabbedPaneUI is extends BasicTabbedPaneUI.
So our inner class is like this,
private class SynthTabbedPaneUIWrapper extends SynthTabbedPaneUI
{
private MouseAdapter menuAdapter;
private MouseAdapter getMenuAdapter()
{
if (menuAdapter == null)
{
menuAdapter =
new MouseAdapter()
{
#Override
public void mouseReleased(final MouseEvent e)
{
//implement to stop right click tab selection
//implement to show pop up menu
}
};
}
}
#Override
protected MouseListener createMouseListener()
{
return getMenuAdapter();
}
}
After that we can set our custom UI object into TabbedPane.
tabbedPane.setUI(new SynthTabbedPaneUIWrapper());

Detecting mouse enter/exit events anywhere on JPanel

Basically there is a JPanel on which I want to know when the mouse enters the area of the JPanel and exits the area of the JPanel. So I added a mouse listener, but if there are components on the JPanel and the mouse goes over one of them it is detected as an exit on the JPanel, even though the component is on the JPanel. I was wondering whether anyone knows any way to solve this problem without doing something like adding listeners onto all components on the JPanel?
There is a very easy solution for this problem that can work :
public class MyJPanel implements MouseListener {
public void mouseExited(MouseEvent e) {
java.awt.Point p = new java.awt.Point(e.getLocationOnScreen());
SwingUtilities.convertPointFromScreen(p, e.getComponent());
if(e.getComponent().contains(p)) {return;}
...//the rest of your code
}
...
}
This way you just ignore the mouseExited event when it occurs on a child element.
Here is one way to do it for a component that may contain other components:
Add a global AWT event listener to get all mouse events. For example:
Toolkit.getDefaultToolkit().addAWTEventListener(
new TargetedMouseHandler( panel ), AWTEvent.MOUSE_EVENT_MASK );
Implement the TargetedMouseHandler to ignore events that aren't sourced by the panel or by one of the panel's children (you can use SwingUtilities.isDescendingFrom to test for this).
Keep track of whether or not the mouse is already within the bounds of your panel. When you get a MouseEvent.MOUSE_ENTERED event in your panel or one of its children, set a flag to true.
When you get a MouseEvent.MOUSE_EXITED event, only reset the flag if the point in the MouseEvent is outside the bounds of your target panel. SwingUtilities.convertPoint and Component.getBounds().contains() will come in handy here.
This is sample code implementing Ash's solution. For me, the JFrame did not detect all exit events properly, but an inner JPanel did, so I passed in two components - one for testing descendants and one for testing the boundary.
Toolkit.getDefaultToolkit().addAWTEventListener(
new TargetedMouseHandler(this, this.jPanel),
AWTEvent.MOUSE_EVENT_MASK);
}
public class TargetedMouseHandler implements AWTEventListener
{
private Component parent;
private Component innerBound;
private boolean hasExited = true;
public TargetedMouseHandler(Component p, Component p2)
{
parent = p;
innerBound = p2;
}
#Override
public void eventDispatched(AWTEvent e)
{
if (e instanceof MouseEvent)
{
if (SwingUtilities.isDescendingFrom(
(Component) e.getSource(), parent))
{
MouseEvent m = (MouseEvent) e;
if (m.getID() == MouseEvent.MOUSE_ENTERED)
{
if (hasExited)
{
System.out.println("Entered");
hasExited = false;
}
} else if (m.getID() == MouseEvent.MOUSE_EXITED)
{
Point p = SwingUtilities.convertPoint(
(Component) e.getSource(),
m.getPoint(),
innerBound);
if (!innerBound.getBounds().contains(p))
{
System.out.println("Exited");
hasExited = true;
}
}
}
}
}
}
A simpeler solution with java 1.8+
public class MyJPanel implements MouseListener {
public void mouseExited(MouseEvent e) {
if(!this.contains(e.getPoint())) {
... //the rest of your code
}
}
...
}
If you want to get all events sent to a top-level window you can add a listener to the glass pane of the JFrame. See getGlassPane.

Categories

Resources