Java’s Nimbus look-and-feel and custom keymaps

I recently discovered that the Nimbus look-and-feel in Java ignores background colour that’s been set using setBackground() on a JEditorPane. Apparently this is not a bug; look-and-feels are allowed to ignore the colour that you specify with setBackground() (though this raises the question of why the method even exists). With Nimbus, it turns out, you can control background painting by setting a “client property” on the editor pane (using putClientProperty()) – specifically, you set the “Nimbus.Overrides” property to a UIDefaults object, which is essentially a property-value map. The “EditorPane[Enabled].backgroundPainter” property is supposed to be set to a BackgroundPainter which Nimbus will then use, though if you set it some other object type (such as a String or even a Color) it will ignore the property and instead paint the background using the colour that was set with setBackground() – which of course raises the question of why it couldn’t do this in the first place, but, meh, whatever; at least we have a solution. (Any properties which Nimbus needs that aren’t in your provided UIDefaults will be inherited from Nimbus’ default property set).

There is (of course) Only One Problem.

Setting the “Nimbus.Overrides” client property apparently prevents any custom keymaps that you might have assigned to the JEditorPane from working. This is true even when the UIDefaults table you provide is empty, implying that the mere act of assigning something to the Nimbus.Overrides property is changing behaviour of the look-and-feel, which is surely broken.

I’ve submitted a bug to Oracle (via bugs.java.com) though the hit rate on getting a response for them hasn’t been 100%, so we’ll see what happens. Once you submit you get the following text:

What’s Next?

We will review your report. Depending upon the completeness of the report and our ability to reproduce the problem, either a new bug will be posted, or we will contact you for further information. You will be notified by email in either case.

… (emphasis mine) – however I’ve submitted reports in the past which got no response. I will update this blog entry if/when I receive an acknowledgement. (A few minutes later – first update! I’ve received an automated email stating that “We are evaluating this report and have stored this with an Review ID: JI-9012303”).

Update 24/05/2104: I still haven’t heard anything, but today I discovered this. So, it looks like this was recognised as a bug, but I, the initial reporter, was never notified at all – this seems like poor form. At least my report wasn’t wasted.

Test case below.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.text.Keymap;


public class TestFrame extends JFrame
{
    public static void main(String [] args) {
        try {
        EventQueue.invokeAndWait(new Runnable() {
           @Override
            public void run()
            {
               try {
                   UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
               }
               catch(Exception e) {
                   e.printStackTrace();
                   System.exit(1);
               }
               
               TestFrame tf = new TestFrame();                              
               tf.pack();
               tf.setVisible(true);
            } 
        });
        }
        catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
    
    public TestFrame()
    {
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        
        /* 
         * Create a frame containing a JEditorPane, and override the action for the space bar to show
         * a dialog.
         */
        
        JEditorPane pp = new JEditorPane();

        UIDefaults defaults = new UIDefaults();

        /** COMMENT THIS LINE TO SEE CORRECT BEHAVIOUR */
        pp.putClientProperty("Nimbus.Overrides", defaults);
        
        JPanel contentPanel = new JPanel();
        contentPanel.setLayout(new BorderLayout());
        setContentPane(contentPanel);
        
        contentPanel.setPreferredSize(new Dimension(400, 300));
        contentPanel.add(pp, BorderLayout.CENTER);
        
        Keymap origKeymap = pp.getKeymap();
        Keymap km = JEditorPane.addKeymap("Test keymap", origKeymap);
        
        km.addActionForKeyStroke(KeyStroke.getKeyStroke(' '), new AbstractAction("SHOW_SPACE") {
           @Override
            public void actionPerformed(ActionEvent e)
            {
               JOptionPane.showMessageDialog(TestFrame.this, "Space!");
            } 
        });
        
        pp.setKeymap(km);
    }
}

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s