kirill-grouchnikov / radiance

Building modern, elegant and fast Swing applications
BSD 3-Clause "New" or "Revised" License
807 stars 89 forks source link

Setting an Integer as the value of a SpinnerNumberModel causes a ClassCastException #392

Closed asvitkine closed 2 years ago

asvitkine commented 2 years ago

Version of Radiance (latest development is 6.0-SNAPSHOT)

4.5.0

Sub-project (Common, Animation, Theming, Component, ...)

Version of Java (current minimum is 9)

11

Version of OS

Windows 10

The issue you're experiencing (expected vs actual, screenshot, stack trace etc)

The following exception is thrown if an Integer is set on a SpinnerNumberModel: https://github.com/triplea-game/triplea/issues/10836

Using a Double fixes the issue: https://github.com/triplea-game/triplea/pull/10846

However, the model is supposed to support any Number, not just Double.

kirill-grouchnikov commented 2 years ago

Looking at https://github.com/triplea-game/triplea/pull/10846/files I can reproduce the crash with this, mixing doubles and integers in the initialization of the spinner:

public class SpinnerCrash {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(new RadianceAutumnLookAndFeel());
                } catch (Throwable t) {}

                JFrame frame = new JFrame();

                frame.setLayout(new FlowLayout(FlowLayout.CENTER));
                SpinnerNumberModel model = new SpinnerNumberModel();
                model.setMaximum(1000.0);
                model.setMinimum(0);
                model.setValue(50);
                model.setStepSize(1);

                JSpinner spinner = new JSpinner(model);
                frame.add(spinner);

                frame.setSize(400, 200);
                frame.setLocationRelativeTo(null);
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}

Note how it needs a mix of at least one double (maximum in here) and the rest to be integers. If everything is doubles, or everything is integers, it works.

Running this code under MetalLookAndFeel doesn't crash, but also makes a spinner that doesn't do anything when you click the buttons - probably since this is not a well defined configuration.

The exception is thrown in the core Swing classes - the basic UI delegate and the number model. The crash is in here. Why doesn't it crash under Metal? Because the DefaultLookup.getBoolean(spinner, this, "Spinner.disableOnBoundaryValues", false) returns false under Metal and true under Radiance. The spinner is still broken under Metal, so just because it doesn't crash doesn't mean it's good.

Radiance sets this key to true in here same as in GtkLookAndFeel. Which means that if you're running the same sample that you have under the system look-and-feel on a Linux machine, you'll get the same exception.

This can be fixed in two places. One is in your code to provide a consistent definition of your number model that uses either all integers or all doubles. The other is in the core Swing.

Closing this on the Radiance side of things.

asvitkine commented 2 years ago

Thanks for looking into it.