Skip to content

Commit

Permalink
Resolve "Improve shape layer selection panel - Round2"
Browse files Browse the repository at this point in the history
Closes #1910

See merge request main/Sumatra!1871

sumatra-commit: 464d45aaa542f89ffbddc8ad24a271ff938d842e
  • Loading branch information
mickmack1213 authored and TIGERs GitLab committed Jul 10, 2024
1 parent 7a4b8a6 commit 1d8c114
Show file tree
Hide file tree
Showing 8 changed files with 524 additions and 44 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,34 @@
import org.apache.commons.lang.StringUtils;

import javax.swing.AbstractButton;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;


/**
Expand All @@ -50,23 +64,21 @@
public class VisualizerPresenter implements ISumatraViewPresenter, IWorldFrameObserver
{
private static final int VISUALIZATION_FPS = 24;

private final ShapeSelectionModel shapeSelectionModel = new ShapeSelectionModel();

@Getter
private final VisualizerPanel viewPanel = new VisualizerPanel();

@Getter
private final VisualizerFieldPresenter fieldPresenter = new VisualizerFieldPresenter(
viewPanel.getFieldPanel()
);

@Setter
private String propertiesPrefix = VisualizerPresenter.class.getCanonicalName() + ".";

private final BallInteractor ballInteractor = new BallInteractor();
private final MediaRecorder mediaRecorder = new MediaRecorder();
private final Set<Object> defaultVisibilityObtained = new HashSet<>();
@Setter
private String propertiesPrefix = VisualizerPresenter.class.getCanonicalName() + ".";
private final PropertyChangeListener dividerPositionChangeListener = this::dividerLocationChanged;
private Thread updateThread;
private boolean firstUpdate = true;


public VisualizerPresenter()
Expand Down Expand Up @@ -116,9 +128,13 @@ public VisualizerPresenter()
viewPanel.getShapeSelectionPanel().getTree().setDigIn(false);
viewPanel.getShapeSelectionPanel().getTree().getCheckBoxTreeSelectionModel()
.addTreeSelectionListener(this::onSelectionChanged);
viewPanel.getShapeSelectionPanel().getTree().addTreeExpansionListener(new MyTreeExpansionListener());
viewPanel.getShapeSelectionPanel().getTree().setCellRenderer(new ShapeTreeCellRenderer());
viewPanel.getShapeSelectionPanel().getExpandAll().addActionListener(this::expandAll);
viewPanel.getShapeSelectionPanel().getCollapseAll().addActionListener(this::collapseAll);
viewPanel.getShapeSelectionPanel().getExpandAll().addActionListener(a -> expandAll());
viewPanel.getShapeSelectionPanel().getCollapseAll().addActionListener(a -> collapseAll());
viewPanel.getShapeSelectionPanel().addLayerFileSaver(this::saveLayersToFile);
viewPanel.getShapeSelectionPanel().addLayerFileOpener(this::openLayersFromFile);
viewPanel.getShapeSelectionPanel().getPresetDef().addActionListener(a -> showOnlyDefaultLayers());

viewPanel.getToolbar().getTurnCounterClockwise().addActionListener(a -> fieldPresenter.turnCounterClockwise());
viewPanel.getToolbar().getTurnClockwise().addActionListener(a -> fieldPresenter.turnClockwise());
Expand All @@ -127,17 +143,27 @@ public VisualizerPresenter()
viewPanel.getToolbar().getRecordVideoSelection().addActionListener(this::recordVideoCurrentSelection);
viewPanel.getToolbar().getTakeScreenshotFull().addActionListener(this::takeScreenshotFull);
viewPanel.getToolbar().getTakeScreenshotSelection().addActionListener(this::takeScreenshotSelection);

viewPanel.getSplitPane().setDividerLocation(0.8);
}


private void setShapeSelectionPanelVisibility(boolean visible)
{
if (!firstUpdate && !visible)
{
viewPanel.getSplitPane().removePropertyChangeListener(
JSplitPane.DIVIDER_LOCATION_PROPERTY,
dividerPositionChangeListener
);
}
viewPanel.getShapeSelectionPanel().setVisible(visible);
if (visible)
if (!firstUpdate && visible)
{
viewPanel.getSplitPane().setDividerLocation(0.8);
var prop = SumatraModel.getInstance().getUserProperty(propertiesPrefix + "dividerLocation", 0.8);
SwingUtilities.invokeLater(() -> viewPanel.getSplitPane().setDividerLocation(prop));
viewPanel.getSplitPane().addPropertyChangeListener(
JSplitPane.DIVIDER_LOCATION_PROPERTY,
dividerPositionChangeListener
);
}
}

Expand Down Expand Up @@ -255,6 +281,22 @@ private void update()
{
log.error("Exception in visualizer updater", e);
}

if (firstUpdate)
{
firstUpdate = false;
setShapeSelectionPanelVisibility(getViewPanel().getToolbar().getShapeSelection().isSelected());
}
}


private void dividerLocationChanged(PropertyChangeEvent event)
{
var width = viewPanel.getSplitPane().getWidth();
var size = viewPanel.getSplitPane().getDividerSize();
var loc = viewPanel.getSplitPane().getDividerLocation();
var position = loc / (double) (width - size);
SumatraModel.getInstance().setUserProperty(propertiesPrefix + "dividerLocation", position);
}


Expand All @@ -267,14 +309,26 @@ private void onSelectionChanged(TreeSelectionEvent treeSelectionEvent)
private void updateVisibility()
{
shapeSelectionModel.getSources().forEach((source, node) -> {
boolean visible = isSelected(node);
boolean visible = isSelected(node, true);
fieldPresenter.setSourceVisibility(source, visible);
});

shapeSelectionModel.getLayers().forEach((layer, node) -> {
boolean visible = isSelected(node);
boolean visible = isSelected(node, true);
fieldPresenter.setShapeLayerVisibility(layer.getId(), visible);
SumatraModel.getInstance().setUserProperty(propertiesPrefix + layer.getId(), String.valueOf(visible));
if (defaultVisibilityObtained.contains(layer))
{
var noDigIn = isSelected(node, false);
SumatraModel.getInstance().setUserProperty(propertiesPrefix + layer.getId(), String.valueOf(noDigIn));
}
});

shapeSelectionModel.getLayerCategories().forEach((category, node) -> {
if (defaultVisibilityObtained.contains(category))
{
var noDigIn = isSelected(node, false);
SumatraModel.getInstance().setUserProperty(propertiesPrefix + category.name(), String.valueOf(noDigIn));
}
});
viewPanel.getShapeSelectionPanel().getTree().updateUI();
}
Expand All @@ -283,7 +337,7 @@ private void updateVisibility()
private void newShapeMap(final ShapeMapSource source, final ShapeMap shapeMap)
{
// select shape map sources by default
shapeSelectionModel.addShapeMapSource(source).ifPresent(this::selectNode);
shapeSelectionModel.addShapeMapSource(source).ifPresent(node -> this.selectShapeMapSource(source, node));

List<DefaultMutableTreeNode> newNodes = shapeMap.getAllShapeLayers().stream()
.map(ShapeMap.ShapeLayer::getIdentifier)
Expand All @@ -292,6 +346,27 @@ private void newShapeMap(final ShapeMapSource source, final ShapeMap shapeMap)
.map(Optional::get)
.toList();
newNodes.forEach(this::setDefaultVisibility);

shapeSelectionModel.getLayerCategories().forEach((category, node) -> {
if (!defaultVisibilityObtained.contains(category))
{
var visible = SumatraModel.getInstance().getUserProperty(propertiesPrefix + category.name(), false);
setNodeSelection(node, visible);
defaultVisibilityObtained.add(category);
}
});
}


private void selectShapeMapSource(ShapeMapSource source, DefaultMutableTreeNode node)
{
if (source.getParent() != null && node.getParent() instanceof DefaultMutableTreeNode parent)
{
selectNode(parent);
} else
{
selectNode(node);
}
}


Expand All @@ -303,13 +378,103 @@ private void setDefaultVisibility(DefaultMutableTreeNode node)
propertiesPrefix + shapeLayer.getId(),
shapeLayer.isVisibleByDefault()
);
if (visible)
setNodeSelection(node, visible);
defaultVisibilityObtained.add(shapeLayer);
expandCollapseDependingOnUserProperty(node);
}
}


private void expandCollapseDependingOnUserProperty(DefaultMutableTreeNode node)
{
var tree = viewPanel.getShapeSelectionPanel().getTree();
var path = new TreePath(node.getPath()).getParentPath();
while (path != null)
{
var expanded = SumatraModel.getInstance().getUserProperty(propertiesPrefix + path, false);
if (expanded)
{
selectNode(node);
if (!tree.isExpanded(path))
{
var constPath = path;
SwingUtilities.invokeLater(() -> tree.expandPath(constPath));
}
} else
{
deselectNode(node);
if (tree.isExpanded(path))
{
var constPath = path;
SwingUtilities.invokeLater(() -> tree.collapsePath(constPath));
}
}
path = path.getParentPath();
}
}


private void showOnlyDefaultLayers()
{
shapeSelectionModel.getAllLayerNonLeafNodes().forEach(this::deselectNode);
for (var entry : shapeSelectionModel.getLayers().entrySet())
{
var layer = entry.getKey();
var node = entry.getValue();

var visible = layer.isVisibleByDefault();
SumatraModel.getInstance().setUserProperty(propertiesPrefix + layer.getId(), visible);
setNodeSelection(node, visible);
}

}


private void saveLayersToFile(File file)
{
var lines = shapeSelectionModel.getAllLayerNodes()
.filter(node -> isSelected(node, false))
.map(node -> new TreePath(node.getPath()).toString())
.toList();
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file)))
{
for (var line : lines)
{
writer.write(line);
writer.newLine();
}
} catch (IOException e)
{
log.error(e);
}
}


private void openLayersFromFile(File file)
{
Set<String> paths;
try (BufferedReader reader = new BufferedReader(new FileReader(file)))
{
paths = reader.lines().collect(Collectors.toSet());
} catch (IOException e)
{
log.error(e);
return;
}
for (var node : shapeSelectionModel.getAllLayerNodes().toList())
{
var path = new TreePath(node.getPath()).toString();
setNodeSelection(node, paths.contains(path));
}
}


private void setNodeSelection(DefaultMutableTreeNode node, boolean select)
{
if (select)
{
selectNode(node);
} else
{
deselectNode(node);
}
}

Expand All @@ -330,25 +495,31 @@ private void deselectNode(DefaultMutableTreeNode node)
}


private void expandAll(ActionEvent e)
private void expandAll()
{
var tree = viewPanel.getShapeSelectionPanel().getTree();
shapeSelectionModel.getAllNonLeafPaths().forEach(tree::expandPath);
shapeSelectionModel.getAllNonLeafPaths().forEach(path -> SwingUtilities.invokeLater(() -> tree.expandPath(path)));
}


private void collapseAll(ActionEvent e)
private void collapseAll()
{
var tree = viewPanel.getShapeSelectionPanel().getTree();
shapeSelectionModel.getAllNonLeafPaths().forEach(tree::collapsePath);
shapeSelectionModel.getAllNonLeafNodes()
.filter(node -> node.getPath().length > 1)
.filter(node -> !(shapeSelectionModel.isLayer(node) && node.getPath().length < 3))
.filter(node -> !(shapeSelectionModel.isSource(node) && node.getPath().length < 2))
.map(DefaultMutableTreeNode::getPath)
.map(TreePath::new)
.forEach(path -> SwingUtilities.invokeLater(() -> tree.collapsePath(path)));
}


private boolean isSelected(DefaultMutableTreeNode node)
private boolean isSelected(DefaultMutableTreeNode node, boolean digIn)
{
var treePath = new TreePath(node.getPath());
return viewPanel.getShapeSelectionPanel().getTree().getCheckBoxTreeSelectionModel()
.isPathSelected(treePath, true);
.isPathSelected(treePath, digIn);
}


Expand Down Expand Up @@ -470,4 +641,39 @@ private void toggleShapeSelection()
}
}
}


private class MyTreeExpansionListener implements TreeExpansionListener
{
@Override
public void treeCollapsed(TreeExpansionEvent event)
{
var obj = event.getPath().getLastPathComponent();
if (obj instanceof DefaultMutableTreeNode node)
{
iterateChildrenToCollapse(node);
}
SumatraModel.getInstance().setUserProperty(propertiesPrefix + event.getPath(), null);
}


private void iterateChildrenToCollapse(DefaultMutableTreeNode node)
{
var children = node.children();
while (children.hasMoreElements())
{
var child = (DefaultMutableTreeNode) children.nextElement();
var path = new TreePath(child.getPath());
SumatraModel.getInstance().setUserProperty(propertiesPrefix + path, null);
iterateChildrenToCollapse(child);
}
}


@Override
public void treeExpanded(TreeExpansionEvent event)
{
SumatraModel.getInstance().setUserProperty(propertiesPrefix + event.getPath(), true);
}
}
}
Loading

0 comments on commit 1d8c114

Please sign in to comment.