Making two JButtons "rollover" in sync

  • Thread starter Brian J. Sayatovic
  • Start date
B

Brian J. Sayatovic

I'd like to make two JButtons rollver in sync. That is, when I mouse over
one JButton and it rolls over, I'd like the second one to also rollover.
Likewise, whenI mouse over the second button, they both shoudl again
rollover.

My first thought was to listen to the button for events it broadcasts when
it rolls over and use that to set the other button's state. However, I
found no method for adding sucha listener to a JButton, and I also found no
way to force a JButton into the rolled-over state. Did I miss it?

My second plan is more basic, but I've implemented it and it partially
works. I add a special mouse listener to each JButton that sends a nearly
identical MouseEvent (I change the source) to the other JButton. I also
realized the hard way that I need to detect a "forwarded" MouseEvent and not
forward it again lest I end up in an infinitely recursive call. Here's the
important part of the code:

class DelegatedMouseEvent extends MouseEvent {
public DelegatedMouseEvent(Component delegate, MouseEvent
delegatedEvent) {
super(
delegate,
delegatedEvent.getID(),
delegatedEvent.getWhen(),
delegatedEvent.getModifiers(),
delegatedEvent.getX(),
delegatedEvent.getY(),
delegatedEvent.getClickCount(),
delegatedEvent.isPopupTrigger(),
delegatedEvent.getButton()
);
}
}

class DelegatingMouseListener extends MouseAdapter {
private final Component
delegate;
public DelegatingMouseListener(Component delegate) {
this.delegate = delegate;
}
public void mouseEntered(MouseEvent event) {
System.out.println("Mouse entered " + event.getSource());
if(!(event instanceof DelegatedMouseEvent)) {
this.enqueueEventForDelegate(event);
}
}
public void mouseExited(MouseEvent event) {
System.out.println("Mouse exited " + event.getSource());
if(!(event instanceof DelegatedMouseEvent)) {
this.enqueueEventForDelegate(event);
}
}
private void enqueueEventForDelegate(MouseEvent event) {
EventQueue queue = delegate.getToolkit().getSystemEventQueue();
queue.postEvent(new DelegatedMouseEvent(this.delegate, event));
}
}

This works except when I have the buttons right next to each other. As the
mouse exits one, it enters the other. Because of the ordering of the
events, it ends up like this:

1. LeftButton: mouse exited (post a copy of the event to RightButton... #3)
2. RightButton: mouse entered (post a copy of the event to LeftButton... #4)
3. RightButton: mouse exited
4. LeftButton: mouse entered

Does anyone have a better solution to this, or if not, a graceful way to
handle the problem case in my above implementation?

Regards,
Brian.
 
K

Kleopatra

Brian J. Sayatovic said:
I'd like to make two JButtons rollver in sync. That is, when I mouse over
one JButton and it rolls over, I'd like the second one to also rollover.
Likewise, whenI mouse over the second button, they both shoudl again
rollover.

My first thought was to listen to the button for events it broadcasts when
it rolls over and use that to set the other button's state. However, I
found no method for adding sucha listener to a JButton, and I also found no
way to force a JButton into the rolled-over state. Did I miss it?

My first try would be a ChangeListener to the buttonModel(s) and
checking/setting the rollover state of the model (did never try it,
though)

Greetings
Jeanette
 
B

Brian J. Sayatovic

I tried this before and couldn't get it to work. It seems that describes
when the JButton's bean-like propertyf or rollover icon is changed through
setRolloverIcon. That is, it changes what icon will be used whenever the
JButton is rolled over, but not at the tiem it is being rolled over.

Regards,
Brian.
 
B

Bill Dennis

Here's some quick code that seems to do what you want:

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;

public class TestRollover extends JFrame implements ChangeListener
{
JButton button1 = new JButton(new ImageIcon("icon1.gif"));
JButton button2 = new JButton(new ImageIcon("icon1.gif"));

public static void main (String args[])
{
new TestRollover();
}

TestRollover()
{
ImageIcon rolloverIcon = new ImageIcon("icon2.gif");

button1.setRolloverIcon(rolloverIcon);
button2.setRolloverIcon(rolloverIcon);

button1.getModel().addChangeListener(this);
button2.getModel().addChangeListener(this);

getContentPane().setLayout(new FlowLayout());
getContentPane().add(button1);
getContentPane().add(button2);

pack();
setVisible(true);
}

public void stateChanged(ChangeEvent e)
{
ButtonModel fromModel = null;
ButtonModel toModel = null;

if (e.getSource() == button1.getModel())
{
fromModel = button1.getModel();
toModel = button2.getModel();
}
else
{
fromModel = button2.getModel();
toModel = button1.getModel();
}

if (fromModel.isRollover() != toModel.isRollover())
{
toModel.setRollover(fromModel.isRollover());
}
}
}

Bill Dennis
 
B

Brian J. Sayatovic

Ahh, now I see.. I was totally oblivious to the fact that JBUttons had
ButtonModels. I know Swing is MVC and even use custom ListModels and
TableModels alll the time, but I've never bothered to look into
ButtonModels.

So following your lead, I have the two buttons tied together with a
ChangeListener that matches their boolean properties for:
- Armed
- Enabled
- Rollover
- Selected

I skipped Pressed becuase when I had it in there, pressing one button would
activate both of them! I only want their non-functional behaviors to follow
each other.

Anyways, I've found an odd quirk I can't resolve. Mouse over, etc. works
just fine. When I press one of the buttons, only that button is pressed
(which is obivous since I disconnected their Pressed properties). Now,
since I had to move my mouse over the buttons in order to press oen of them,
they were BOTH in the rollover state before I started my mouse press. NOw
the one being pressed is depressed. If I slide off the button to cancel my
press, the second button is un-rolled-over while the first reverts to
rolled-over.

I'm not sure how they're achieving this out-of-sync behavior.

Any ideas?
 
K

Kleopatra

Brian J. Sayatovic said:
Ahh, now I see.. I was totally oblivious to the fact that JBUttons had
ButtonModels. I know Swing is MVC and even use custom ListModels and
TableModels alll the time, but I've never bothered to look into
ButtonModels.

So following your lead, I have the two buttons tied together with a
ChangeListener that matches their boolean properties for:
- Armed
- Enabled
- Rollover
- Selected

I skipped Pressed becuase when I had it in there, pressing one button would
activate both of them! I only want their non-functional behaviors to follow
each other.

Instead of using a ChangeListener you could try to use a shared
ButtonModel for the state you want to share: then let each button have a
model that delegates every access to the common state to the shared
model but handles everything else quite normally.

Greetings
Jeanette
 
B

Bill Dennis

Brain,
Using the code I posted, I am unable to duplicate the behavior that
you described. Can you create this behavior using my code alone, or
are you seeing the behavior when the code is included in your larger
application?

Bill Dennis
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,188
Latest member
Crypto TaxSoftware

Latest Threads

Top