mgarin / weblaf

WebLaF is a fully open-source Look & Feel and component library written in pure Java for cross-platform desktop Swing applications.
http://weblookandfeel.com
GNU General Public License v3.0
1.14k stars 235 forks source link

desktoppane needs more than 'Light skin' #529

Closed mokun closed 5 years ago

mokun commented 5 years ago

Hi,

I use a JDesktopPane for an app.

Sometimes (but not all the time) when I start the app in Eclipse, it complains about the StyleException as follows :

Exception in thread "pool-2-thread-5" com.alee.managers.style.StyleException: Skin 'Light skin' doesn't have any styles for component type: desktoppane[class javax.swing.JDesktopPane, DesktopPaneUI, class com.alee.laf.desktoppane.WebDesktopPaneUI, class com.alee.laf.desktoppane.WebDesktopPaneUI, StyleId [ id: 'desktoppane'; parent: null ]]
    at com.alee.managers.style.data.SkinInfo.getStyle(SkinInfo.java:496)
    at com.alee.managers.style.XmlSkin.getStyle(XmlSkin.java:161)
    at com.alee.managers.style.AbstractSkin.applySkin(AbstractSkin.java:64)
    at com.alee.managers.style.StyleData.applySkin(StyleData.java:280)
    at com.alee.managers.style.StyleManager.installSkin(StyleManager.java:1125)
    at com.alee.laf.desktoppane.WebDesktopPaneUI.installUI(WebDesktopPaneUI.java:70)
    at java.desktop/javax.swing.JComponent.setUI(JComponent.java:685)
    at java.desktop/javax.swing.JDesktopPane.setUI(JDesktopPane.java:169)
    at java.desktop/javax.swing.JDesktopPane.updateUI(JDesktopPane.java:240)
    at org.mars_sim.msp.ui.swing.MainDesktopPane.uiPulse(MainDesktopPane.java:1386)

In my MainDesktopPane, I already initializes weblaf with

            WebLookAndFeel.install();
            UIManagers.initialize();

However, it's when I call super.updateUI() inside an overriding method that it creates this Exception.

    @Override
    public void uiPulse(double time) {
        super.updateUI();
        updateWindows();
    }

What's the workaround on this ?

Thanks !

mgarin commented 5 years ago

This sounds a lot like a usage of Swing component outside of Event Dispatch Thread (EDT). I think it might be the case mostly because you said:

... Sometimes (but not all the time) ...

And also log says:

Exception in thread "pool-2-thread-5"

Which certainly doesn't sound like it's an EDT, although that particular call might not be the only thing causing the issue - usually it is a mix of things being called in parallel, like multiple UI updates or repaints.

If you aren't familiar with EDT - you can read more about it here: Event Dispatch Thread

Generally speaking - any UI-related code must be executed on EDT with no exceptions. While Swing does not enforce that and usually runs without exceptions - it is not guaranteed and usually causes a lot of random exceptions in larger applications or some unique cases.

I couldn't really reproduce the exception you've shown on a standalone example:

public class DesktopTest
{
    public static void main ( final String[] args )
    {
        SwingUtilities.invokeLater ( new Runnable ()
        {
            @Override
            public void run ()
            {
                try
                {
                    WebLookAndFeel.install ();

                    final JDesktopPane desktopPane = new JDesktopPane ();
                    desktopPane.setPreferredSize ( new Dimension ( 600, 600 ) );

                    final JInternalFrame frame = new JInternalFrame ( "Frame", true, false, true, true );
                    final JButton update = new JButton ( "Sample" );
                    update.addActionListener ( new ActionListener ()
                    {
                        @Override
                        public void actionPerformed ( final ActionEvent e )
                        {
                            desktopPane.updateUI ();
                        }
                    } );
                    frame.add ( update );
                    desktopPane.add ( frame );
                    frame.setClosed ( false );
                    frame.setIcon ( false );
                    frame.setLocation ( 25,25 );
                    frame.pack ();
                    frame.setVisible ( true );

                    TestFrame.show ( desktopPane );
                }
                catch ( final PropertyVetoException ignored )
                {
                    //
                }
            }
        } );
    }
}

Note that i do perform all UI operations within EDT in this example, including code in ActionListener - it just doesn't need to be queued because all Swing component events are already fired within EDT and you can simply perform UI-related operations in the listener directly.


So, what I would suggest first - try enabling this option:

WebLookAndFeel.setForceSingleEventsThread ( true );

Right at the start of your application, before you install L&F or create any UI elements. If you have any Swing code running outside of EDT - it will throw exceptions and halt further execution of the Swing-related code.

If you want to simply receive exceptions but allow UI to continue running - you can also setup a different handler for those exceptions, for instance this one:

WebLookAndFeel.setNonEventThreadHandler ( new NonEventThreadHandler ()
{
    @Override
    public void handle ( final RuntimeException e )
    {
        LoggerFactory.getLogger ( DesktopTest.class ).error ( e.getMessage (), e );
    }
} );

You can also read more about this option here: Forced EDT usage

Once it's set - try running your application, if there is anything wrong with UI code usage - you would usually get a handful of exceptions pointing at exact locations of the non-EDT calls that must be invoked within EDT. Once you find them - you'll need to move their execution to EDT, sometimes it's simple, sometimes it's complicated - all depends on how your application code is written and structured so I can't really give any specific advice here.

Once you have everything running smooth and without any exceptions thrown - WebLaF exception you've shown should go as well. But if the exception is still there - then it might have something to do with your application code in particular. Some tricky component use case or some additional code that interferes with component or it's UI logic. Or maybe a faulty style. There could be quite a few issues and I can't really tell what it is exactly without a standalone example that would reproduce the issue.

mgarin commented 5 years ago

Just a side not about EDT - it's usage necessity is not dictated by WebLaF, but by Swing itself. You can read the official Oracle guide about Event Dispatch Thread usage it here: https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

mokun commented 5 years ago

Yes as soon as I wrap around my setLookAndFeel() inside SwingUtilities.invokeLater(), the problem is gone. setLookAndFeel() is where I declare the use of weblaf.

Thanks !

mgarin commented 5 years ago

Glad that helped :)

Swing can be unpredictable when used outside of EDT, so I do strongly recommend following the practice of running all UI-related code in EDT - that should save you a lot of time in the future.