<Swing Dev> Possible bug - buttons do not regain rollover state after reenabling
Alexander Potochkin
Alexander.Potochkin at Sun.COM
Tue Jan 22 16:40:06 UTC 2008
Hello Martin
Your observation is correct,
Swing doesn't count a button as rollovered
if it was disabled and enabled again
(actually I haven't seen a GUI library
which is ready for such situations)
This problem very rare affects anybody
so I'd see it as a Swing limitation
(however limitation is a bit too strong word for this case)
While it is definitely possible to fix this bug
I doubt it worth doing, because in this case
we'll have to fix tons of other cases when a component
was moved/disabled/hidden and shown again
which is not reasonable
Here is a workaround for your test case:
private void disableButtonTemporarily() {
button.setEnabled(false);
Timer timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
button.setEnabled(true);
Point l = MouseInfo.getPointerInfo().getLocation();
Point buttonLocationToScreen = new Point();
SwingUtilities.convertPointToScreen(buttonLocationToScreen, button);
if (new Rectangle(buttonLocationToScreen,
button.getSize()).contains(l)) {
button.getModel().setRollover(true);
}
}
});
timer.setRepeats(false);
timer.start();
}
Thanks
alexp
> Hi,
>
> Sorry to intrude without a proper introduction. I just want some insight
> before reporting a possible bug.
>
> A friend of mine asked me to help him with a problem he has with a swing
> app. In this app some buttons are disabled for a short period of time,
> after which, if the user has not moved the mouse, the button doesn't
> show the rollover state after becoming enabled again. I tested this on
> Java6 update 3, using this quick&dirty test:
>
> ------------
>
> import java.awt.event.*;
> import javax.swing.*;
>
> public class RolloverTest extends JFrame {
> private JButton button;
>
> public static void main(final String[] args) {
> SwingUtilities.invokeLater(new Runnable() {
> public void run() {
> new RolloverTest();
> }
> });
> }
>
> public RolloverTest() {
> super("Rollover Test");
> createButton();
> setDefaultCloseOperation(EXIT_ON_CLOSE);
> pack();
> setVisible(true);
> }
>
> private void createButton() {
> button = new JButton("Press me");
> button.addActionListener(new ActionListener() {
> public void actionPerformed(ActionEvent e) {
> disableButtonTemporarily();
> }
> });
> add(button);
> }
>
> private void disableButtonTemporarily() {
> button.setEnabled(false);
> Timer timer = new Timer(1000, new ActionListener() {
> public void actionPerformed(ActionEvent e) {
> button.setEnabled(true);
> }
> });
> timer.setRepeats(false);
> timer.start();
> }
> }
>
> -----------
>
> Sorry I couldn't test it on the latest codebase, I don't have an
> environment set up for that purpose. Please check if you can that this
> also happens with later versions of Swing or the JDK.
>
> Also, I did some digging in the source at openjdk to see what could be
> the problem. Since it happens not only with JButton, but also to
> JMenuItem, JCheckBox, etc, at first I thought that the problem might be
> in the DefaultButtonModel, that the flag was simply lost when the button
> is disabled. But apparently DefaultButtonModel.setEnabled() unsets other
> flags but not the rollover state, although
> DefaultButtonModel.setRollover() prevents changes to the rollover flag
> if the button is disabled. In consequence, I couldn't manage a
> workaround by providing a different ButtonModel.
>
> It seems that AbstractButton.setEnabled() forces the rollover state to
> change before disabling the button. There's no comment on the source as
> to why this state change is handled by AbstractButton and not by the
> ButtonModel, as happens with the other states. Is there a reason why
> AbstractButton.setEnabled() has to handle the rollover and not
> ButtonModel.setEnabled()? It seems that this is due to the order of how
> event propagation is to occur, but I'm uncertain.
>
> ----------
>
> setEnabled in AbstractButton:
>
> public void setEnabled(boolean b) {
> if (!b && model.isRollover()) {
> model.setRollover(false);
> }
> super.setEnabled(b);
> model.setEnabled(b);
> }
>
> setEnabled in DefaultButtonModel:
>
> public void setEnabled(boolean b) {
> if(isEnabled() == b) {
> return;
> }
>
> if (b) {
> stateMask |= ENABLED;
> } else {
> stateMask &= ~ENABLED;
> // unarm and unpress, just in case
> stateMask &= ~ARMED;
> stateMask &= ~PRESSED;
> }
>
>
> fireStateChanged();
> }
>
> ----------
>
> If AbstractButton didn't force the rollover to false, and let the
> ModelButton handle this, an implementation of ModelButton could store
> the right state of the rollover even though it would be superseded by
> the enabled state ( i.e. although rollover is true, it would be shown as
> false if enabled is false). Therefore, when the button is reenabled, the
> rollover could be restored as expected. But that's only if there isn't a
> special reason why AbstractButton has to handle the rollover state
> before disabling the button.
>
> That's all. Sorry for the long mail.
>
> Best Regards,
>
> Martin Alterisio
More information about the swing-dev
mailing list