I was fiddling with the Netbeans Visual Library again, trying to show a complex graph on screen and I run into a problem: There were so many connection in the graph that some widgets were hard to read because the ConnectionWidget
arrows were printed over them.
So I tried to I try to rearrange the order of my LayerWidgets
to paint the LabelWidgets on top of ConnectionWidgets. A call to mainLayer.bringToFront()
should be enough, but this is what I got when I tried that:
java.lang.IllegalStateException: Widget (org.netbeans.api.visual.widget.LabelWidget@10014f0) was not added into the scene
at org.netbeans.api.visual.anchor.Anchor.getRelatedSceneLocation(Anchor.java:213)
at org.netbeans.modules.visual.anchor.RectangularAnchor.compute(RectangularAnchor.java:111)
at org.netbeans.modules.visual.router.DirectRouter.routeConnection(DirectRouter.java:65)
at org.netbeans.api.visual.widget.ConnectionWidget.calculateRouting(ConnectionWidget.java:527)
at org.netbeans.modules.visual.layout.ConnectionWidgetLayout.layout(ConnectionWidgetLayout.java:109)
at org.netbeans.api.visual.widget.Widget.layout(Widget.java:1350)
at org.netbeans.api.visual.widget.Widget.layout(Widget.java:1342)
at org.netbeans.api.visual.widget.LayerWidget.layout(LayerWidget.java:86)
at org.netbeans.api.visual.widget.Widget.layout(Widget.java:1342)
at org.netbeans.api.visual.widget.Scene.layoutScene(Scene.java:312)
at org.netbeans.api.visual.widget.Scene.validate(Scene.java:393)
at org.netbeans.api.visual.widget.SceneComponent.addNotify(SceneComponent.java:92)
at java.awt.Container.addImpl(Container.java:1039)
at java.awt.Container.add(Container.java:896)
at com.rubenlaguna.modules.mainmodule.GraphTopComponent.setDotFile(GraphTopComponent.java:80)
at com.rubenlaguna.modules.mainmodule.DotOpenSupport.createCloneableTopComponent(DotOpenSupport.java:31)
at org.openide.windows.CloneableOpenSupport.openCloneableTopComponent(CloneableOpenSupport.java:197)
at org.openide.windows.CloneableOpenSupport$1.run(CloneableOpenSupport.java:98)
at org.openide.util.Mutex.doEvent(Mutex.java:1335)
at org.openide.util.Mutex.writeAccess(Mutex.java:452)
at org.openide.windows.CloneableOpenSupport.open(CloneableOpenSupport.java:95)
at org.openide.actions.OpenAction.performAction(OpenAction.java:81)
at org.openide.util.actions.NodeAction$DelegateAction$1.run(NodeAction.java:589)
at org.netbeans.modules.openide.util.ActionsBridge.doPerformAction(ActionsBridge.java:77)
at org.openide.util.actions.NodeAction$DelegateAction.actionPerformed(NodeAction.java:585)
at org.openide.explorer.view.TreeView$PopupSupport.mouseClicked(TreeView.java:1515)
at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:253)
at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:252)
at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:252)
at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:252)
at java.awt.Component.processMouseEvent(Component.java:6137)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
at java.awt.Component.processEvent(Component.java:5899)
at java.awt.Container.processEvent(Container.java:2023)
at java.awt.Component.dispatchEventImpl(Component.java:4501)
at java.awt.Container.dispatchEventImpl(Container.java:2081)
at java.awt.Component.dispatchEvent(Component.java:4331)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4301)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3974)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3895)
at java.awt.Container.dispatchEventImpl(Container.java:2067)
at java.awt.Window.dispatchEventImpl(Window.java:2458)
at java.awt.Component.dispatchEvent(Component.java:4331)
[catch] at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
So I posted a question on netbeans graph mailing list but the suggestions there didn’t resolved my problem.
After digging a little bit into the Visual Library source code I come up with a really easy solution. Just call mainLayer.bringToFront()
AFTER the scene was painted for the first time on the screen. I should have tried that first!. The result shows LabelWidgets
painted overt the ConnectionWidgets
so the text in the LabelWidgets
is always readable.
class GraphTopComponent extends CloneableTopComponent {
//.... omitted ....
private showScene() {
JComponent c = scene.createView();
GraphLayout graphLayout = GraphLayoutFactory.createHierarchicalGraphLayout(scene, false);
ForceDirectedLayout forceDirectedGraphLayout = new ForceDirectedLayout(scene);
SceneLayout sceneGraphLayout = LayoutFactory.createSceneGraphLayout(scene, graphLayout);
sceneGraphLayout.invokeLayout();
this.forceDirectedSceneLayout = LayoutFactory.createSceneGraphLayout(scene, forceDirectedGraphLayout);
jPanel1.removeAll();
jPanel1.add©;
scene.bringMainLayerToFront();
}
}
class DotGraphScene extends GraphScene.StringGraph {
//... omitted ...
void bringMainLayerToFront() {
mainLayer.bringToFront();
}
}
Anchor will throw an IllegalStateException
if the relatedWidget has no location in the scene. Those widget will not get a position until the LayerWidget
that contains them is processed. So you keep the original order of the LayerWidgets until the scene is painted for the first time, by then all widgets have a location in the scene and then it’s posible to change the relative order of the LayerWidgets
using LayerWidget.bringToFront()
.
The only problem with this approach is that is your are using a dynamic layout like me there is a chance that the layout changes the position of the LabelWidget
by a noticiable amount and the Anchor will not be updated accordingly until the next iteration of the dynamic layout. Which lead to the artifact marked in red in the previous figure. This is only a real problem if your dynamic layout doesn’t stabilize or if is not very smooth.