cbfiddle / Issues

0 stars 0 forks source link

NullPointerException getting font metrics from AquaButtonUI during initialisation #5

Open hakanai opened 5 years ago

hakanai commented 5 years ago

AWT's FontDesignMetrics is apparently unable to deal with the font on a component being set to null when the metrics are fetched.

JIDE's NullButton class deliberately sets the font on the button to null. In JIDE's docs for said class, they say that this doesn't necessarily mean that getFont() will return null, because it falls back to getting the font from the parent. However, when the component is first initialised, it has not yet been added to a container, so there is no place to get the fallback font from, and this exception ends up being thrown during component initialisation, when installing VAqua's button UI.

This ultimately means that apps using JIDE's status bar components can't use VAqua because the exception will be thrown before the application is displayed on the screen.

java.lang.NullPointerException
    at sun.font.FontDesignMetrics$MetricsKey.init(FontDesignMetrics.java:217)
    at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:286)
    at sun.swing.SwingUtilities2.getFontMetrics(SwingUtilities2.java:1113)
    at javax.swing.JComponent.getFontMetrics(JComponent.java:1626)
    at org.violetlib.aqua.AquaButtonUI.getPreferredContentSize(Unknown Source)
    at org.violetlib.aqua.AquaButtonBorder.isProposedButtonWidgetUsable(Unknown Source)
    at org.violetlib.aqua.AquaPushButtonBorder.getButtonWidget(Unknown Source)
    at org.violetlib.aqua.AquaPushButtonBorder.getButtonWidget(Unknown Source)
    at org.violetlib.aqua.AquaButtonBorder.determineLayoutConfiguration(Unknown Source)
    at org.violetlib.aqua.AquaButtonUI.configure(Unknown Source)
    at org.violetlib.aqua.AquaButtonUI.installDefaults(Unknown Source)
    at javax.swing.plaf.basic.BasicButtonUI.installUI(BasicButtonUI.java:88)
    at org.violetlib.aqua.AquaButtonUI.installUI(Unknown Source)
    at javax.swing.JComponent.setUI(JComponent.java:666)
    at javax.swing.AbstractButton.setUI(AbstractButton.java:1810)
    at javax.swing.JButton.updateUI(JButton.java:147)
    at javax.swing.AbstractButton.init(AbstractButton.java:2176)
    at javax.swing.JButton.<init>(JButton.java:137)
    at javax.swing.JButton.<init>(JButton.java:91)
    at com.jidesoft.swing.NullButton.<init>(Unknown Source)
cbfiddle commented 5 years ago

I am not able to duplicate this bug.

My reading of the code is that AquaButtonUI.installDefaults should ensure that the button has a font and that the font is saved in a client property which is used by AquaButtonUI.getGenericDefaultFont.

There must be something more complicated going on than is revealed by my simple test program.

Alan

public class TestButtonWithNoFont { public TestButtonWithNoFont() { UIManager.getLookAndFeel();

    try {
        UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
    } catch (Exception ex) {
        System.err.println("Unable to install VAqua");
    }

    JButton b = new JButton("Test");
    b.setFont(null);

    JFrame fr = new JFrame();
    fr.add(b);
    fr.pack();
    fr.setVisible(true);
}

public static void main(String[] args)
{
    SwingUtilities.invokeLater(() -> new TestButtonWithNoFont());
}

}

On Sep 24, 2019, at 11:34 PM, Trejkaz (pen name) notifications@github.com wrote:

AWT's FontDesignMetrics is apparently unable to deal with the font on a component being set to null when the metrics are fetched.

JIDE's NullButton class deliberately sets the font on the button to null. In JIDE's docs for said class, they say that this doesn't necessarily mean that getFont() will return null, because it falls back to getting the font from the parent. However, when the component is first initialised, it has not yet been added to a container, so there is no place to get the fallback font from, and this exception ends up being thrown during component initialisation, when installing VAqua's button UI.

This ultimately means that apps using JIDE's status bar components can't use VAqua because the exception will be thrown before the application is displayed on the screen.

java.lang.NullPointerException at sun.font.FontDesignMetrics$MetricsKey.init(FontDesignMetrics.java:217) at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:286) at sun.swing.SwingUtilities2.getFontMetrics(SwingUtilities2.java:1113) at javax.swing.JComponent.getFontMetrics(JComponent.java:1626) at org.violetlib.aqua.AquaButtonUI.getPreferredContentSize(Unknown Source) at org.violetlib.aqua.AquaButtonBorder.isProposedButtonWidgetUsable(Unknown Source) at org.violetlib.aqua.AquaPushButtonBorder.getButtonWidget(Unknown Source) at org.violetlib.aqua.AquaPushButtonBorder.getButtonWidget(Unknown Source) at org.violetlib.aqua.AquaButtonBorder.determineLayoutConfiguration(Unknown Source) at org.violetlib.aqua.AquaButtonUI.configure(Unknown Source) at org.violetlib.aqua.AquaButtonUI.installDefaults(Unknown Source) at javax.swing.plaf.basic.BasicButtonUI.installUI(BasicButtonUI.java:88) at org.violetlib.aqua.AquaButtonUI.installUI(Unknown Source) at javax.swing.JComponent.setUI(JComponent.java:666) at javax.swing.AbstractButton.setUI(AbstractButton.java:1810) at javax.swing.JButton.updateUI(JButton.java:147) at javax.swing.AbstractButton.init(AbstractButton.java:2176) at javax.swing.JButton.(JButton.java:137) at javax.swing.JButton.(JButton.java:91) at com.jidesoft.swing.NullButton.(Unknown Source) — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/cbfiddle/Issues/issues/5?email_source=notifications&email_token=ABSSJGJI7UUKLGPQRT7VFFDQLMA73A5CNFSM4I2IKD4KYY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4HNQCJSQ, or mute the thread https://github.com/notifications/unsubscribe-auth/ABSSJGNXILBOJVPTF4ZFZFLQLMA73ANCNFSM4I2IKD4A.

hakanai commented 5 years ago

Here's a standalone reproduction with class copied from JIDE's NullButton. https://github.com/jidesoft/jide-oss/blob/master/src/com/jidesoft/swing/NullButton.java

import java.awt.Color;
import java.awt.Font;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.FontUIResource;

public class TestButtonWithNoFont {
    private TestButtonWithNoFont() {
        UIManager.getLookAndFeel();

        try {
            UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
        } catch (Exception ex) {
            System.err.println("Unable to install VAqua");
        }

        JButton b = new NullButton("Test");
        b.setFont(null);

        JFrame fr = new JFrame();
        fr.add(b);
        fr.pack();
        fr.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(TestButtonWithNoFont::new);
    }

    // Copy of JIDE's class to remove dependency on JIDE
    private static class NullButton extends JButton {
        private NullButton(String title) {
            super(title);
            clearAttribute();
        }

        private void clearAttribute() {
            super.setFont(null);
            super.setBackground(null);
            super.setForeground(null);
        }

        @Override
        public void setFont(Font var1) {
            if (!(var1 instanceof FontUIResource)) {
                super.setFont(var1);
            }
        }

        @Override
        public void setBackground(Color var1) {
            if (!(var1 instanceof ColorUIResource)) {
                super.setBackground(var1);
            }
        }

        @Override
        public void setForeground(Color var1) {
            if (!(var1 instanceof ColorUIResource)) {
                super.setForeground(var1);
            }
        }
    }
}
hakanai commented 5 years ago

They have a few other "null component" classes with the same general behaviour. I haven't tested any of the others yet. A few of them are essentially specialised buttons though so there is a non-zero chance of them having similar issues.

cbfiddle commented 5 years ago

Thank you for the test case.

I can fix the NPE, but that may be only the tip of the iceberg.

What VAqua is trying to do is choose a native button style that is compatible with the button font. Several of the native button styles have fixed heights, and so cannot be used if the font is too large.

VAqua relies on property change events to detect when a button font is changed. Inheriting a font allows the font to be changed without generating an event, which is unsupported.

If an application is using one of the null component classes to control the font and colors of the component, that suggests to me that the developer does not want to use a native style. Perhaps it would be wise to install a basic UI instead of using the VAqua UI?

On Oct 21, 2019, at 4:07 PM, Trejkaz (pen name) notifications@github.com wrote:

They have a few other "null component" classes with the same general behaviour. I haven't tested any of the others yet. A few of them are essentially specialised buttons though so there is a non-zero chance of them having similar issues.

NullCheckBox NullJideButton NullLabel NullPanel NullRadioButton NullTristateCheckBox — You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/cbfiddle/Issues/issues/5?email_source=notifications&email_token=ABSSJGPCOLNBTDGUO6JT6QDQPYY2BA5CNFSM4I2IKD4KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEB4CMQA#issuecomment-544745024, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSSJGNITIV4DUKFQAQFAVLQPYY2BANCNFSM4I2IKD4A.

hakanai commented 5 years ago

We're currently leaning in that direction, yes. Even if we do get everything up and running on VAqua, we have to support Windows as well, and we were abusing some of their private look and feel classes too, so we'll either have to find a similarly modern look and feel that looks like Windows, or go looking for a basic or synth-based look and feel for Windows anyway. If we have to go looking anyway, maybe we should give up on looking native and try to get something which is just good enough not to be offensive to the eyes.