diff --git a/src/main/java/com/marginallyclever/ro3/apps/RO3Frame.java b/src/main/java/com/marginallyclever/ro3/apps/RO3Frame.java index 70645709b..2ba01a81a 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/RO3Frame.java +++ b/src/main/java/com/marginallyclever/ro3/apps/RO3Frame.java @@ -11,6 +11,7 @@ import com.formdev.flatlaf.FlatLightLaf; import com.marginallyclever.communications.application.TextInterfaceToSessionLayer; import com.marginallyclever.ro3.RO3; +import com.marginallyclever.ro3.Registry; import com.marginallyclever.ro3.UndoSystem; import com.marginallyclever.ro3.apps.about.AboutPanel; import com.marginallyclever.ro3.apps.actions.*; @@ -277,7 +278,6 @@ private void createLayout() { detailView.add(nodeDetailView, BorderLayout.CENTER); Docking.dock(detailView, treeView, DockingRegion.SOUTH); windows.add(detailView); - nodeTreeView.addSelectionChangeListener(nodeDetailView); DockingPanel logView = new DockingPanel("5e565f83-9734-4281-9828-92cd711939df","Log"); logView.add(logPanel, BorderLayout.CENTER); diff --git a/src/main/java/com/marginallyclever/ro3/apps/nodedetailview/NodeDetailView.java b/src/main/java/com/marginallyclever/ro3/apps/nodedetailview/NodeDetailView.java index d26f3b493..ccb97d3b7 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/nodedetailview/NodeDetailView.java +++ b/src/main/java/com/marginallyclever/ro3/apps/nodedetailview/NodeDetailView.java @@ -1,5 +1,8 @@ package com.marginallyclever.ro3.apps.nodedetailview; +import com.marginallyclever.ro3.Registry; +import com.marginallyclever.ro3.listwithevents.ItemAddedListener; +import com.marginallyclever.ro3.listwithevents.ItemRemovedListener; import com.marginallyclever.ro3.node.Node; import com.marginallyclever.ro3.apps.nodetreeview.SelectionChangeListener; import org.slf4j.Logger; @@ -13,7 +16,8 @@ /** * {@link NodeDetailView} is a panel that displays the details of a class that implements {@link Node}. */ -public class NodeDetailView extends JPanel implements SelectionChangeListener { +public class NodeDetailView extends JPanel + implements SelectionChangeListener, ItemAddedListener, ItemRemovedListener { private static final Logger logger = LoggerFactory.getLogger(NodeDetailView.class); private final JScrollPane scroll = new JScrollPane(); @@ -65,4 +69,28 @@ public void selectionChanged(List selectedNodes) { this.revalidate(); this.repaint(); } + + @Override + public void addNotify() { + super.addNotify(); + Registry.selection.addItemAddedListener(this); + Registry.selection.addItemRemovedListener(this); + } + + @Override + public void removeNotify() { + super.removeNotify(); + Registry.selection.removeItemAddedListener(this); + Registry.selection.removeItemRemovedListener(this); + } + + @Override + public void itemAdded(Object source,Node item) { + selectionChanged(Registry.selection.getList()); + } + + @Override + public void itemRemoved(Object source,Node item) { + selectionChanged(Registry.selection.getList()); + } } diff --git a/src/main/java/com/marginallyclever/ro3/apps/nodetreeview/NodeTreeView.java b/src/main/java/com/marginallyclever/ro3/apps/nodetreeview/NodeTreeView.java index 44bd31c5e..fcccfa25a 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/nodetreeview/NodeTreeView.java +++ b/src/main/java/com/marginallyclever/ro3/apps/nodetreeview/NodeTreeView.java @@ -4,6 +4,8 @@ import com.marginallyclever.ro3.SceneChangeListener; import com.marginallyclever.ro3.apps.actions.AddNode; import com.marginallyclever.ro3.apps.actions.RemoveNode; +import com.marginallyclever.ro3.listwithevents.ItemAddedListener; +import com.marginallyclever.ro3.listwithevents.ItemRemovedListener; import com.marginallyclever.ro3.node.Node; import com.marginallyclever.ro3.node.NodeAttachListener; import com.marginallyclever.ro3.node.NodeDetachListener; @@ -13,6 +15,7 @@ import javax.swing.*; import javax.swing.event.EventListenerList; +import javax.swing.event.TreeSelectionEvent; import javax.swing.tree.*; import java.awt.*; import java.security.InvalidParameterException; @@ -23,13 +26,16 @@ /** * {@link NodeTreeView} is a panel that displays the node tree. */ -public class NodeTreeView extends JPanel implements NodeAttachListener, NodeDetachListener, NodeRenameListener, SceneChangeListener { +public class NodeTreeView extends JPanel + implements NodeAttachListener, NodeDetachListener, NodeRenameListener, + SceneChangeListener, ItemAddedListener, ItemRemovedListener { private static final Logger logger = LoggerFactory.getLogger(NodeTreeView.class); private final JTree tree; private final NodeTreeBranch treeModel = new NodeTreeBranch(Registry.getScene()); private final EventListenerList listenerList = new EventListenerList(); private final JToolBar toolBar = new JToolBar(); private final RemoveNode removeNode = new RemoveNode(this); + private boolean isExternalChange = false; public NodeTreeView() { super(); @@ -57,26 +63,31 @@ private void setupTree() { tree.setCellRenderer(cellRender); tree.setCellEditor(new NodeTreeBranchEditor(tree, cellRender)); tree.setTransferHandler(new NodeTreeTransferHandler()); - tree.addTreeSelectionListener((e) ->{ - // single selection - TreePath path = e.getPath(); - if(path==null) { - // no selection - removeNode.setEnabled(false); - fireSelectionChangeEvent(List.of()); - } else { - NodeTreeBranch selectedNode = (NodeTreeBranch) path.getLastPathComponent(); - // scene root cannot be deleted. - removeNode.setEnabled(selectedNode != treeModel.getRoot()); - fireSelectionChangeEvent(List.of(selectedNode.getNode())); - } - }); + tree.addTreeSelectionListener(this::changeSelection); + } + + private void changeSelection(TreeSelectionEvent e) { + if(isExternalChange) return; + + Registry.selection.removeAll(); + removeNode.setEnabled(false); + // handle many selections + for(TreePath path : e.getPaths()) { + NodeTreeBranch selectedNode = (NodeTreeBranch) path.getLastPathComponent(); + // scene root cannot be deleted. + removeNode.setEnabled(selectedNode != treeModel.getRoot()); + Registry.selection.add(selectedNode.getNode()); + } } @Override public void addNotify() { super.addNotify(); Registry.addSceneChangeListener(this); + Registry.selection.addItemAddedListener(this); + Registry.selection.addItemRemovedListener(this); + + listenTo(Registry.getScene()); } @@ -91,6 +102,8 @@ public void removeNotify() { super.removeNotify(); stopListeningTo(Registry.getScene()); Registry.removeSceneChangeListener(this); + Registry.selection.removeItemAddedListener(this); + Registry.selection.removeItemRemovedListener(this); } private void stopListeningTo(Node node) { @@ -220,7 +233,6 @@ public void beforeSceneChange(Node oldScene) { //logger.debug("beforeSceneChange"); stopListeningTo(oldScene); tree.clearSelection(); // does not trigger selection change event? - fireSelectionChangeEvent(List.of()); removeNode.setEnabled(false); } @@ -271,4 +283,28 @@ public void removeSelectedNodes() { } // else root node, can't remove. } } + + @Override + public void itemAdded(Object source,Node item) { + isExternalChange = true; + try { + var branch = findTreeNode(item); + if (branch == null) throw new InvalidParameterException("item not found in tree " + item.getAbsolutePath()); + tree.addSelectionPath(new TreePath(branch.getPath())); + } finally { + isExternalChange = false; + } + } + + @Override + public void itemRemoved(Object source,Node item) { + isExternalChange = true; + try { + var branch = findTreeNode(item); + if(branch==null) throw new InvalidParameterException("item not found in tree "+item.getAbsolutePath()); + tree.removeSelectionPath(new TreePath(branch.getPath())); + } finally { + isExternalChange = false; + } + } } diff --git a/src/main/java/com/marginallyclever/ro3/apps/render/Viewport.java b/src/main/java/com/marginallyclever/ro3/apps/render/Viewport.java index f5037a81e..c4ec2e5f9 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/render/Viewport.java +++ b/src/main/java/com/marginallyclever/ro3/apps/render/Viewport.java @@ -170,7 +170,7 @@ private void addRenderPassSelection() { button.setToolTipText("Select the render passes to use."); for(RenderPass renderPass : renderPasses.getList()) { - addRenderPass(renderPass); + addRenderPass(null,renderPass); } } @@ -192,12 +192,12 @@ public void removeNotify() { renderPasses.removeItemRemovedListener(this::removeRenderPass); } - private void addRenderPass(RenderPass renderPass) { + private void addRenderPass(Object source,RenderPass renderPass) { addRenderPassInternal(renderPass); addGLEventListener(renderPass); } - private void removeRenderPass(RenderPass renderPass) { + private void removeRenderPass(Object source,RenderPass renderPass) { removeRenderPassInternal(renderPass); removeGLEventListener(renderPass); } @@ -237,13 +237,13 @@ private void removeRenderPassInternal(RenderPass renderPass) { } } - private void addCamera(Camera camera) { + private void addCamera(Object source,Camera camera) { if(cameraListModel.getIndexOf(camera) == -1) { cameraListModel.addElement(camera); } } - private void removeCamera(Camera camera) { + private void removeCamera(Object source,Camera camera) { cameraListModel.removeElement(camera); } diff --git a/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawMeshes.java b/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawMeshes.java index 0033d3061..a1889caa2 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawMeshes.java +++ b/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawMeshes.java @@ -281,7 +281,7 @@ private void drawAllMeshes(GL3 gl3, List meshes, Camera camera) { // https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping private Matrix4d getLightSpaceMatrix() { // orthographic projection from the light's point of view - Matrix4d lightProjection = MatrixHelper.orthographicMatrix4d(-10,10,-10,10,1.0,100.0); + Matrix4d lightProjection = MatrixHelper.orthographicMatrix4d(-10,10,-10,10,1.0,75); // look at the scene from the light's point of view Matrix4d lightView = MatrixHelper.lookAt(fromLightUnit, // from new Vector3d(0,0,0), // to diff --git a/src/main/java/com/marginallyclever/ro3/listwithevents/ItemAddedListener.java b/src/main/java/com/marginallyclever/ro3/listwithevents/ItemAddedListener.java index dfb52c78e..b27a34462 100644 --- a/src/main/java/com/marginallyclever/ro3/listwithevents/ItemAddedListener.java +++ b/src/main/java/com/marginallyclever/ro3/listwithevents/ItemAddedListener.java @@ -3,5 +3,5 @@ import java.util.EventListener; public interface ItemAddedListener extends EventListener { - void itemAdded(T item); + void itemAdded(Object source,T item); } \ No newline at end of file diff --git a/src/main/java/com/marginallyclever/ro3/listwithevents/ItemRemovedListener.java b/src/main/java/com/marginallyclever/ro3/listwithevents/ItemRemovedListener.java index 2e7ce41d6..6209ef5fb 100644 --- a/src/main/java/com/marginallyclever/ro3/listwithevents/ItemRemovedListener.java +++ b/src/main/java/com/marginallyclever/ro3/listwithevents/ItemRemovedListener.java @@ -3,5 +3,5 @@ import java.util.EventListener; public interface ItemRemovedListener extends EventListener { - void itemRemoved(T item); + void itemRemoved(Object source,T item); } \ No newline at end of file diff --git a/src/main/java/com/marginallyclever/ro3/listwithevents/ListWithEvents.java b/src/main/java/com/marginallyclever/ro3/listwithevents/ListWithEvents.java index 6cb6e6a0a..b4de8682b 100644 --- a/src/main/java/com/marginallyclever/ro3/listwithevents/ListWithEvents.java +++ b/src/main/java/com/marginallyclever/ro3/listwithevents/ListWithEvents.java @@ -2,7 +2,6 @@ import javax.swing.event.EventListenerList; import java.util.ArrayList; -import java.util.Collection; import java.util.List; /** @@ -41,13 +40,13 @@ public void removeItemRemovedListener(ItemRemovedListener listener) { protected void fireItemAdded(T item) { for (ItemAddedListener l : listenerList.getListeners(ItemAddedListener.class)) { - l.itemAdded(item); + l.itemAdded(this,item); } } protected void fireItemRemoved(T item) { for (ItemRemovedListener l : listenerList.getListeners(ItemRemovedListener.class)) { - l.itemRemoved(item); + l.itemRemoved(this,item); } } @@ -60,4 +59,8 @@ public void removeAll() { remove(list.get(0)); } } + + public int size() { + return list.size(); + } } \ No newline at end of file