Are there any public examples of modules that implement multiple Nav tree nodes for their designer workspace?
i.e. Perspective has the Style workspace and the View workspace
I have been using the TabbedResourceWorkspace, but that handles all of the Project resource nav tree work for me, and seems to be restricting the ability to customize the root node of the workspace.
I started digging around in the other workspace types, and playing with context.getProjectBrowserRoot()#addChild(), but when I register my TabbedResourceWorkspace its automatically adding the project browser node, and then I end up with a duplicate node.
I am mostly working off this 10 year old comment, so who knows if this has been updated and I am on the wrong path.
If anyone knows of any examples that would be great, or just a few comments on how to get headed in the right direction.
I am slowly making my way through this, but essentially I started with what @bmusson and so far this is what I have:
I created an implementation of the AbstractNavTreeNode with no children, this will be the parent node
I then created two classes (one for each node I want) that extend TabbedResourceWorkspace
In my constructor for the workspaces, I made the parent node a constructor arg, and then override getNavTreeNodeParent to return that provided parent node
I register my parent node in the designer hook with this.context.getProjectBrowserRoot()#addChild
I then register each workspace with this.context#registerResourceWorkspace
At this point, I have a node element with two child nodes. Progress! I will now refer to these as the A node and the B node
I created a class that extends WorkspaceWelcomePanel in a way that is abstract between my two nodes, so that both the A and B nodes can share the same panel, though they each create their own.
That's as far as I have gotten... now I am in "which class Is initializing in what order, and where does this mysterious NPE come from" purgatory...
Will update when I have more useful steps.
EDIT:
The NPE for prosperities sake:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at com.inductiveautomation.ignition.designer.tabbedworkspace.TabbedResourceWorkspace.registerNavTreeNode(TabbedResourceWorkspace.java:206)
at com.inductiveautomation.ignition.designer.tabbedworkspace.TabbedResourceWorkspace.lambda$new$0(TabbedResourceWorkspace.java:125)
at java.desktop/java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at com.inductiveautomation.ignition.designer.tabbedworkspace.TabbedResourceWorkspace.registerNavTreeNode(TabbedResourceWorkspace.java:206)
at com.inductiveautomation.ignition.designer.tabbedworkspace.TabbedResourceWorkspace.lambda$new$0(TabbedResourceWorkspace.java:125)
at java.desktop/java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
This should extend MutableNavTreeNode, so you can manually add your child nodes.
Are you manually adding these nodes as children of your root node? Ensure you attach them to the parent after you've added the root node to the project browser root.
I'm interested in how to accomplish this. My current implementation has workspaces for each resource, but some of them would definitely be better as tabs of a single workspace.
Taking a shot in the dark - does this involve AbstractNavTreeNode#getWorkspaceName() and ResourceWorkspace#getKey()?
I guess maybe the question is, am I attaching these incorrectly, because I feel like it's the right order?
Currently in my designer startup I am doing this:
// Intialize the root navigation node in the project browser
ConfigManagerNavTreeNode configManagerNavTreeNode = new ConfigManagerNavTreeNode();
this.context.getProjectBrowserRoot().addChild(configManagerNavTreeNode);
// Create and register my workspaces
ProjectConfigWorkspace projectWorkspace = new ProjectConfigWorkspace(this.context, configManagerNavTreeNode);
GatewayConfigWorkspace gatewayWorkspace = new GatewayConfigWorkspace(this.context, configManagerNavTreeNode);
this.context.registerResourceWorkspace(projectWorkspace);
this.context.registerResourceWorkspace(gatewayWorkspace);
// Add my workspaces to the top level navTree
configManagerNavTreeNode.addChild(gatewayWorkspace.getRootNode());
configManagerNavTreeNode.addChild(projectWorkspace.getRootNode());
Then in the constructor for my workspaces, I am doing
This is the one that just showed up when I started the designer the last time
13:08:10.525 [Designer-Startup] INFO designer.main - Starting module: Process Config Manager [+6695]
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at com.inductiveautomation.ignition.designer.navtree.model.MutableNavTreeNode.add(MutableNavTreeNode.java:37)
at com.inductiveautomation.ignition.designer.navtree.model.MutableNavTreeNode.lambda$addChild$0(MutableNavTreeNode.java:25)
at java.desktop/java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at com.inductiveautomation.ignition.designer.navtree.model.MutableNavTreeNode.add(MutableNavTreeNode.java:37)
at com.inductiveautomation.ignition.designer.navtree.model.MutableNavTreeNode.lambda$addChild$0(MutableNavTreeNode.java:25)
at java.desktop/java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
13:08:10.538 [Designer-Startup] INFO designer.main - Initializing Scripting... [+6708]
This also happens when the designer hook starts up, before it starts initializing scripting.
EDIT: This looks like its a different NPE, however I am still not sure what’s causing it since I don’t see my package anywhere in this stack trace
Look for a call to com.inductiveautomation.ignition.designer.navtree.model.MutableNavTreeNode#addChild; you’re providing a null (or not-yet-constructed?) node to addChild, which is the cause of that NPE.
// Intialize the root navigation node in the project browser
ConfigManagerNavTreeNode configManagerNavTreeNode = new ConfigManagerNavTreeNode();
this.context.getProjectBrowserRoot().addChild(configManagerNavTreeNode);
// Create and register my workspaces
ProjectConfigWorkspace projectWorkspace = new ProjectConfigWorkspace(this.context, configManagerNavTreeNode);
GatewayConfigWorkspace gatewayWorkspace = new GatewayConfigWorkspace(this.context, configManagerNavTreeNode);
this.context.registerResourceWorkspace(projectWorkspace);
this.context.registerResourceWorkspace(gatewayWorkspace);
// Add my workspaces to the top level navTree
configManagerNavTreeNode.addChild(gatewayWorkspace.getRootNode());
configManagerNavTreeNode.addChild(projectWorkspace.getRootNode());
Isn’t this: configManagerNavTreeNode.addChild(gatewayWorkspace.getRootNode());
Trying to add configManagerNavTreeNode to itself, as a child?
Again, at a glance, but I don’t think it would be too hard to extend TabbedResourceWorkspace to support different editors. You would just have to conditionally return the appropriate editor from newResourceEditor(ResourcePath).
As I have gotten a little further, I am realizing I don’t see an obvious construct for “Gateway scoped resources” just “Project scoped resources”. Before I go down the rabbit hole of managing my own resource folders on the gateway, is there already something in place for this?