kirill-grouchnikov / substance

A modern and high-performant Swing look-and-feel library
163 stars 110 forks source link

Switching from Substance to another L&F causes IllegalArgumentException #72

Closed frankb33 closed 6 years ago

frankb33 commented 6 years ago

Version of Substance

7.1.01

Version of Java

Oracle 1.8.0_151

Version of OS

Xubuntu 16.04.3 (kernel : 4.13.0-32-generic / os.arch : amd64

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

in my app I have chosen a substance L&F (with windows decorating painted by substance) and have open a JDialog and the main JFrame. When I switch to another L&F (in this case Synthetica, also using own windows decorating) I get an "Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Substance delegate used when Substance is not the current LAF [component JTextField in window bgblitz.EditCurPosition:'Edit Position' under Synthetica BlackMoon Look and Feel]" when no JDialog is open no exception occurs. I use simply UIManager.setLookAndFeel, nothing else. Any idea? (BTW the "known Issues" link on your main page is an 404)

I attached the trace. And important: Thanks a lot for your work on substance. I looked at it a decade (or even more ago) and doesn't used it. I stumbled upon it recently again and was deeply impressed. Boy has it grown! best Frank

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Substance delegate used when Substance is not the current LAF [component JTextField in window bgblitz.EditCurPosition:'Edit Position' under Synthetica BlackMoon Look and Feel] at org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities.traceSubstanceApiUsage(SubstanceCoreUtilities.java:1933) at org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities.getColorScheme(SubstanceColorSchemeUtilities.java:222) at org.pushingpixels.substance.internal.utils.SubstanceColorUtilities.getDefaultBackgroundColor(SubstanceColorUtilities.java:817) at org.pushingpixels.substance.internal.utils.SubstanceColorUtilities.getBackgroundFillColor(SubstanceColorUtilities.java:737) at org.pushingpixels.substance.internal.utils.SubstanceTextUtilities.getTextBackgroundFillColor(SubstanceTextUtilities.java:502) at org.pushingpixels.substance.internal.utils.SubstanceTextUtilities.paintTextCompBackground(SubstanceTextUtilities.java:492) at org.pushingpixels.substance.internal.ui.SubstanceTextFieldUI.paintBackground(SubstanceTextFieldUI.java:156) at javax.swing.plaf.basic.BasicTextUI.paintSafely(BasicTextUI.java:726) at javax.swing.plaf.basic.BasicTextUI.paint(BasicTextUI.java:881) at javax.swing.plaf.basic.BasicTextUI.update(BasicTextUI.java:860) at org.pushingpixels.substance.internal.ui.SubstanceTextFieldUI.update(SubstanceTextFieldUI.java:285) at javax.swing.JComponent.paintComponent(JComponent.java:780) at javax.swing.JComponent.paint(JComponent.java:1056) at javax.swing.JComponent.paintToOffscreen(JComponent.java:5210) at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:290) at javax.swing.RepaintManager.paint(RepaintManager.java:1272) at javax.swing.JComponent._paintImmediately(JComponent.java:5158) at javax.swing.JComponent.paintImmediately(JComponent.java:4969) at javax.swing.RepaintManager$4.run(RepaintManager.java:831) at javax.swing.RepaintManager$4.run(RepaintManager.java:814) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80) at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814) at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789) at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738) at javax.swing.RepaintManager.access$1200(RepaintManager.java:64) at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756) at java.awt.EventQueue.access$500(EventQueue.java:97) at java.awt.EventQueue$3.run(EventQueue.java:709) at java.awt.EventQueue$3.run(EventQueue.java:703) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80) at java.awt.EventQueue.dispatchEvent(EventQueue.java:726) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

frankb33 commented 6 years ago

Sorry, for wasting your time. I haven't touched this code for a VERY long time. SwingUtilities.updateComponentTreeUI on all open dialogs fixed the issue. A classical RTFM :(

kirill-grouchnikov commented 6 years ago

SubstanceCortex.GlobalScope.setSkin() in the upcoming 8.0 (or SubstanceLookAndFeel.setSkin in pre-8.0) will call SwingUtilities.updateComponentTreeUI on all application windows.

frankb33 commented 6 years ago

Hi Kiril,

SubstanceCortex.GlobalScope.setSkin() in the upcoming 8.0 (or SubstanceLookAndFeel.setSkin in pre-8.0) will call SwingUtilities.updateComponentTreeUI on all application windows.

Thanks for your quick response. I found another issue with secondary menus (although they are not shown at the time of the LaF-switch?? And difficult to reproduce. I have to switch between the themes at least a dozen times or so), so I ask the user for a application restart when switching between Substance and Synthetica themes (in one “family” all it smooth) as I did it already between decorated and undecorated windows (I use substance only without native decoration). It’s just a little inconvenience for the user and IMHO far better than an occasionally malfunction.

BTW I’m very glad that there is support for Swing and in your case really excellent. I have a spare time project for quite some time and it is simply too much work to switch to JavaFX (even if the migration path is less difficult) and I love polished surfaces :)

Best Frank

P.S. If you enjoy playing Backgammon I send you a free license.

kirill-grouchnikov commented 6 years ago

Is it the same stack trace with secondary-level menus?

In general, since I came back to Substance, my line of thinking is that as an application developer it is much better for the visual polish and consistency of the app to choose one look-and-feel and one skin in that look-and-feel, and integrate tightly with that specific appearance.

You can see that in the list of customizations that Substance supports with SubstanceCortex, that Synthetica supports with their APIs and that WebLaf does with theirs.

I'm not saying that Substance specifically should not fully support switching to and from other look-and-feels. It's just that supporting that at the application level (some preference or switch) means that you are losing out at the additional features that you can get from your look-and-feel of choice.

Having said that, if you have a small standalone app that shows the issue you're experiencing with menus under the latest versions of Substance and Synthetica, re-open this and I'll take a look.

kirill-grouchnikov commented 6 years ago

(accidentally clicked "Comment" in the middle of replying. Read the previous one fully in case you have only seen only that early part of it)

frankb33 commented 6 years ago

I appended the trace (just in case). I just read a bit in the Synthetica documentation. They recommend to restart the application, when switching to a non Synthetica LaF, so I guess thats the safe choice.

I provide so many boards with different styles (see here http://www.bgblitz.com/themes.html or here http://www.dcbackgammon.com) that switching the LaF for a matching LaF is the cream of the crop :) The fun fact: most users stick with the default theme...

Best Frank

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: de.javasoft.plaf.synthetica.SyntheticaRootPaneUI cannot be cast to org.pushingpixels.substance.internal.ui.SubstanceRootPaneUI at org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities.getTitlePane(SubstanceCoreUtilities.java:1700) at org.pushingpixels.substance.api.painter.SubstancePainterUtils.getOffsetInRootPaneCoords(SubstancePainterUtils.java:49) at org.pushingpixels.substance.api.painter.decoration.ArcDecorationPainter.paintExtraBackground(ArcDecorationPainter.java:162) at org.pushingpixels.substance.api.painter.decoration.ArcDecorationPainter.paintDecorationArea(ArcDecorationPainter.java:69) at org.pushingpixels.substance.internal.painter.DecorationPainterUtils.paintDecorationBackground(DecorationPainterUtils.java:223) at org.pushingpixels.substance.internal.painter.DecorationPainterUtils.paintDecorationBackground(DecorationPainterUtils.java:182) at org.pushingpixels.substance.internal.painter.BackgroundPaintingUtils.update(BackgroundPaintingUtils.java:106) at org.pushingpixels.substance.internal.ui.SubstancePopupMenuUI.paint(SubstancePopupMenuUI.java:147) at javax.swing.plaf.ComponentUI.update(ComponentUI.java:161) at javax.swing.JComponent.paintComponent(JComponent.java:780) at javax.swing.JComponent.paint(JComponent.java:1056) at javax.swing.JComponent.paintChildren(JComponent.java:889) at javax.swing.JComponent.paint(JComponent.java:1065) at javax.swing.JComponent.paintChildren(JComponent.java:889) at javax.swing.JComponent.paint(JComponent.java:1065) at javax.swing.JLayeredPane.paint(JLayeredPane.java:586) at javax.swing.JComponent.paintChildren(JComponent.java:889) at javax.swing.JComponent.paintToOffscreen(JComponent.java:5217) at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1579) at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1502) at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:306) at javax.swing.RepaintManager.paint(RepaintManager.java:1272) at javax.swing.JComponent.paint(JComponent.java:1042) at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39) at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:79) at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:116) at java.awt.Container.paint(Container.java:1975) at java.awt.Window.paint(Window.java:3904) at javax.swing.RepaintManager$4.run(RepaintManager.java:842) at javax.swing.RepaintManager$4.run(RepaintManager.java:814) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80) at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814) at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789) at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738) at javax.swing.RepaintManager.access$1200(RepaintManager.java:64) at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756) at java.awt.EventQueue.access$500(EventQueue.java:97) at java.awt.EventQueue$3.run(EventQueue.java:709) at java.awt.EventQueue$3.run(EventQueue.java:703) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80) at java.awt.EventQueue.dispatchEvent(EventQueue.java:726) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) at java.awt.EventDispatchThread.run(EventDispatchThread.java:82) Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: de.javaso

kirill-grouchnikov commented 6 years ago

This looks like going from Synthetica to Substance misses updating at least one Window. SwingUtilities.updateComponentTreeUI should be called on all the Windows in your app.

frankb33 commented 6 years ago

I do SwingUtilities.updateComponentTreeUI on the main window and (since the weekend :)) on all open Dialogs. I also don't use heavy weight Popups in case they are used for nested menus :( I just tried it out again, about 25 changes, all went well including the correct look, and all code is in the AWT-Event-Thread so a race condition/threadig issue on the application side seems to be not the case. Weird...

kirill-grouchnikov commented 6 years ago

Interesting. So it starts in SubstancePopupMenuUI.paint and then goes with the generic painting sequence. Probably can shortcut the title pane lookup there since it's not relevant for popup menus, but that doesn't sound like the right way to go - since it would be addressing the symptom.

But there might not be an API to enumerate all the open popup menus. Having said that, perhaps the code that switches the look-and-feel in your app should wait to run until after the menu click has been processed and the popups have been dismissed? Maybe as a separate event on the EDT and not right in the action listener?

kirill-grouchnikov commented 6 years ago

There's also some code in https://stackoverflow.com/questions/8643672/list-all-jpopupmenu-currently-shown (first answer) to find all active popup menus. Probably there you'd apply updateComponentTreeUI on each one.

frankb33 commented 6 years ago

That led me to the right direction to reproduce and fix the issue. When the board was small enough so that the popup was larger than the window, the popup was a heavy component and the ComponentTreeUI wasn't updated. The stack overflow hint fixed it and was additionally simpler :) Invoke later seems to fix another small artefact which happens only with one Synthetica LaF so everything is fine now 👍 Thanks a lot.