简体   繁体   中英

JPopupMenu in JFrame using AWTUtilities.setWindowOpaque(window, false) using synth L&F not appearing

This one has me quite puzzled. Basically I am developing a multi window application using transparent shaped windows using a custom Synth L&F. Parts of the application invoke JFrame / JDialog components from the parent frame. Within these components I have pop up menus and comboboxes, the problem is that some people using the applications experience the pop up menus not appearing when invoked. There are no exceptions and the code executes fine including for popup menus 'show' method.

I have tried to nail this down to OS specifics without much joy, apart from there doesn't seem to be an issue on mac OSX. Some windows users such as myself experience no problems, others do....

Also I have tracked down the offending line of code which sets the window opacity:

AWTUtilities.setWindowOpaque(window, false) 

If I remove this LOC then the popup windows appear fine. In addition replacing this LOC with :

window.setBackground(new Color(0.0f, 0.0f, 0.0f, 0.0f));

produces the same issue. Another thing is that if I use the default L&F the popups render ok.

Just to confirm the issue is the same for both JFrame and JDialog components, and was just wondering if anybody else has either come across this issue or could point me in the direction of the possible cause.

Cheers

Test source to reproduce:

import com.sun.awt.AWTUtilities;

import javax.swing.*;
import javax.swing.plaf.synth.SynthLookAndFeel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class TestFrame extends JFrame{

    public TestFrame(){
        super.setTitle("Test Frame");

        JButton btnDialog = new JButton("Open Dialog");
        btnDialog.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                TestDialog dialog = new TestDialog(TestFrame.this, true);
                dialog.setVisible(true);
            }
        });

        super.add(btnDialog, BorderLayout.CENTER);
        super.pack();
        super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        super.setVisible(true);
    }

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

    public static void initLookAndFeel() {
        SynthLookAndFeel lookAndFeel = new SynthLookAndFeel();
        try {
            lookAndFeel.load(TestFrame.class.getResourceAsStream("/testskin.xml"), TestFrame.class);
            UIManager.setLookAndFeel(lookAndFeel);
        }
        catch (Exception e) {
           e.printStackTrace();
        }
    }

    public static class TestDialog extends JDialog{

        public TestDialog(Frame owner, boolean modal) {
            super(owner, modal);

            JComboBox petList = new JComboBox(new String[] { "Bird", "Cat", "Dog", "Rabbit", "Pig" });
            super.add(petList, BorderLayout.CENTER);

            super.setUndecorated(true);
            AWTUtilities.setWindowOpaque(this, false);
            super.pack();
        }
    }
}

and the testskin.xml:

<synth>

    <style id="backingStyle">
        <opaque value="true"/>
        <font name="Dialog" size="14"/>
    </style>
    <bind style="backingStyle" type="region" key=".*"/>

    <style id="ComboBox List Renderer">
        <opaque value="true"/>
        <state value="ENABLED">
            <color type="TEXT_FOREGROUND" value="#000000"/>
        </state>
        <state value="DISABLED">
            <color type="TEXT_FOREGROUND" value="#999999"/>
        </state>
        <state value="SELECTED">
            <color type="TEXT_FOREGROUND" value="#CC6600"/>
            <color type="TEXT_BACKGROUND" value="#FFEEDD"/>
        </state>
    </style>
    <bind style="ComboBox List Renderer" type="name" key="ComboBox.listRenderer" />

    <style id="Combo Box">
        <property key="ComboBox.showPopupOnNavigation" type="boolean" value="true"/>
        <state>
            <color value="#D8D987" type="BACKGROUND"/>
        </state>
    </style>
    <bind style="Combo Box" type="region" key="ComboBox" />

</synth>

As mentioned removing:

AWTUtilities.setWindowOpaque(window, false) 

makes the combobox pop up menu render ok, in addition adding a default background to all styles (under style="backingStyle") eg:

<state>
   <color value="#D8D987" type="BACKGROUND"/>
</state>

will at least make the pop menu appear, however it is still not renderd properly. I have tried this on three seperate windows xp virtual machines, all experience the same issues. In addition do not think I mentiod this but it was built on JDK 7, and run on equivalent JRE in all cases. I myself on windows 7 ultimate 64 bit experience no problems, another user using windows 7 premium 64 bit does experience the same issues.


Some progress, The paint method for the pop up menu components fails to invoke if:

AWTUtilities.setWindowOpaque(window, false) 

is set. Manually calling repaint, updateUI, revalidate after calling the 'show' method will make the pop up menu render ok. For combox elements setting a custom UI and overiding the 'createPopup' method, with a class extending javax.swing.plaf.basic.BasicComboPopup that invokes repaint/updateUI/revalidate on show eg :

 public class ComboPopup extends BasicComboPopup {

    public ComboPopup( JComboBox combo ) {
        super(combo);
    }

    @Override
    public void show(Component invoker, int x, int y) {
        super.show(invoker, x, y);
        this.updateUI();
    }
}

will make the combox menu render ok. However I am yet to find a workaround for submneu (JMenu) items for a popup since the popup is created within a private method. This seems like a bug, but if I am doing something wrong could someone let me know :)

Cheers

Jonathan

I answered a similar question here . Hoping it's helpful to others who find this thread, I'll put my workaround here too.

Basically your problem is presenting whenever you need a HeavyWeightPopup -- a popup that doesn't fit within the target window. The workaround is to invoke a repaint after any popup displays. Simply invoke the following code when you launch your application.

PopupFactory.setSharedInstance(new PopupFactory() 
{
    @Override
    public Popup getPopup(Component owner, final Component contents, int x, int y) throws IllegalArgumentException
    {
        Popup popup = super.getPopup(owner, contents, x, y);
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                contents.repaint();
            }
        });
        return popup;
    }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM