[#2485] Add component visibility functionality
This commit is contained in:
parent
b37d694a34
commit
7d71a9367e
@ -147,6 +147,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
|
||||
// If true, component change events will not be fired
|
||||
private boolean bypassComponentChangeEvent = false;
|
||||
|
||||
/**
|
||||
* Controls the visibility of the component. If false, the component will not be rendered.
|
||||
* Visibility does not affect component simulation.
|
||||
*/
|
||||
private boolean isVisible = true;
|
||||
|
||||
|
||||
/**
|
||||
* Used to invalidate the component after calling {@link #copyFrom(RocketComponent)}.
|
||||
@ -2707,6 +2713,22 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this component is visible.
|
||||
* @return True if this component is visible.
|
||||
*/
|
||||
public boolean isVisible() {
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the component's visibility to the specified value.
|
||||
* @param value Visibility value
|
||||
*/
|
||||
public void setVisible(boolean value) {
|
||||
this.isVisible = value;
|
||||
fireComponentChangeEvent(ComponentChangeEvent.GRAPHIC_CHANGE);
|
||||
}
|
||||
|
||||
/////////// Iterators //////////
|
||||
|
||||
|
@ -95,6 +95,9 @@ public class ComponentRenderer {
|
||||
if (glu == null)
|
||||
throw new IllegalStateException(this + " Not Initialized");
|
||||
|
||||
if (!c.isVisible()) {
|
||||
return;
|
||||
}
|
||||
glu.gluQuadricNormals(q, GLU.GLU_SMOOTH);
|
||||
|
||||
if (c instanceof BodyTube) {
|
||||
|
@ -52,8 +52,6 @@ import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import info.openrocket.core.file.wavefrontobj.export.OBJExportOptions;
|
||||
import info.openrocket.core.file.wavefrontobj.export.OBJExporterFactory;
|
||||
import info.openrocket.core.file.wavefrontobj.CoordTransform;
|
||||
import info.openrocket.core.file.wavefrontobj.DefaultCoordTransform;
|
||||
import info.openrocket.core.logging.ErrorSet;
|
||||
import info.openrocket.core.logging.WarningSet;
|
||||
import info.openrocket.core.appearance.DecalImage;
|
||||
@ -257,6 +255,7 @@ public class BasicFrame extends JFrame {
|
||||
|
||||
popupMenu.addSeparator();
|
||||
popupMenu.add(actions.getScaleAction());
|
||||
popupMenu.add(actions.getToggleVisibilityAction());
|
||||
|
||||
popupMenu.addSeparator();
|
||||
popupMenu.add(actions.getExportOBJAction());
|
||||
@ -608,6 +607,10 @@ public class BasicFrame extends JFrame {
|
||||
item = new JMenuItem(actions.getScaleAction());
|
||||
editMenu.add(item);
|
||||
|
||||
item = new JMenuItem(actions.getToggleVisibilityAction());
|
||||
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_COMMA, SHORTCUT_KEY));
|
||||
editMenu.add(item);
|
||||
|
||||
|
||||
//// Preferences
|
||||
item = new JMenuItem(trans.get("main.menu.edit.preferences"));
|
||||
|
@ -5,11 +5,7 @@ import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.Serial;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
@ -18,8 +14,11 @@ import javax.swing.JOptionPane;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import info.openrocket.core.rocketcomponent.*;
|
||||
import info.openrocket.swing.gui.configdialog.ComponentConfigDialog;
|
||||
import info.openrocket.swing.gui.dialogs.ScaleDialog;
|
||||
import info.openrocket.swing.gui.util.GUIUtil;
|
||||
import info.openrocket.swing.gui.util.Icons;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@ -31,12 +30,6 @@ import info.openrocket.core.document.OpenRocketDocument;
|
||||
import info.openrocket.core.document.Simulation;
|
||||
import info.openrocket.core.l10n.Translator;
|
||||
import info.openrocket.core.logging.Markers;
|
||||
import info.openrocket.core.rocketcomponent.ComponentChangeEvent;
|
||||
import info.openrocket.core.rocketcomponent.ComponentChangeListener;
|
||||
import info.openrocket.core.rocketcomponent.ParallelStage;
|
||||
import info.openrocket.core.rocketcomponent.Rocket;
|
||||
import info.openrocket.core.rocketcomponent.RocketComponent;
|
||||
import info.openrocket.core.rocketcomponent.AxialStage;
|
||||
import info.openrocket.core.startup.Application;
|
||||
import info.openrocket.core.util.ORColor;
|
||||
import info.openrocket.core.util.Pair;
|
||||
@ -61,6 +54,8 @@ public class RocketActions {
|
||||
public static final KeyStroke EDIT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_E,
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx());
|
||||
public static final KeyStroke DELETE_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0);
|
||||
public static final KeyStroke VISIBILITY_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_COMMA,
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx());
|
||||
|
||||
private final OpenRocketDocument document;
|
||||
private final Rocket rocket;
|
||||
@ -81,6 +76,7 @@ public class RocketActions {
|
||||
private final RocketAction moveUpAction;
|
||||
private final RocketAction moveDownAction;
|
||||
private final RocketAction exportOBJAction;
|
||||
private final RocketAction toggleVisibilityAction;
|
||||
private static final Translator trans = Application.getTranslator();
|
||||
private static final Logger log = LoggerFactory.getLogger(RocketActions.class);
|
||||
|
||||
@ -106,6 +102,7 @@ public class RocketActions {
|
||||
this.moveUpAction = new MoveUpAction();
|
||||
this.moveDownAction = new MoveDownAction();
|
||||
this.exportOBJAction = new ExportOBJAction();
|
||||
this.toggleVisibilityAction = new ToggleVisibilityAction();
|
||||
|
||||
OpenRocketClipboard.addClipboardListener(new ClipboardListener() {
|
||||
@Override
|
||||
@ -154,6 +151,7 @@ public class RocketActions {
|
||||
moveUpAction.clipboardChanged();
|
||||
moveDownAction.clipboardChanged();
|
||||
exportOBJAction.clipboardChanged();
|
||||
toggleVisibilityAction.clipboardChanged();
|
||||
}
|
||||
|
||||
|
||||
@ -207,6 +205,10 @@ public class RocketActions {
|
||||
return exportOBJAction;
|
||||
}
|
||||
|
||||
public Action getToggleVisibilityAction() {
|
||||
return toggleVisibilityAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tie an action to a JButton, without using the icon or text of the action for the button.
|
||||
*
|
||||
@ -1257,4 +1259,83 @@ public class RocketActions {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to toggle the visibility of the selected components.
|
||||
*/
|
||||
private class ToggleVisibilityAction extends RocketAction {
|
||||
public ToggleVisibilityAction() {
|
||||
this.putValue(NAME, trans.get("RocketActions.VisibilityAct.Hide"));
|
||||
this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.VisibilityAct.ttip.Hide"));
|
||||
this.putValue(SMALL_ICON, GUIUtil.getUITheme().getVisibilityHiddenIcon());
|
||||
this.putValue(MNEMONIC_KEY, KeyEvent.VK_COMMA);
|
||||
this.putValue(ACCELERATOR_KEY, VISIBILITY_KEY_STROKE);
|
||||
clipboardChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clipboardChanged() {
|
||||
var components = new ArrayList<>(selectionModel.getSelectedComponents());
|
||||
super.setEnabled(!components.isEmpty());
|
||||
|
||||
if (!components.isEmpty()) {
|
||||
var firstComponent = components.get(0);
|
||||
|
||||
if (components.size() > 1 || isRocketOrStage(firstComponent)) {
|
||||
if (!firstComponent.isVisible()) {
|
||||
this.putValue(NAME, trans.get("RocketActions.VisibilityAct.ShowAll"));
|
||||
this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.VisibilityAct.ttip.ShowAll"));
|
||||
} else {
|
||||
this.putValue(NAME, trans.get("RocketActions.VisibilityAct.HideAll"));
|
||||
this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.VisibilityAct.ttip.HideAll"));
|
||||
}
|
||||
} else {
|
||||
if (!firstComponent.isVisible()) {
|
||||
this.putValue(NAME, trans.get("RocketActions.VisibilityAct.Show"));
|
||||
this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.VisibilityAct.ttip.Show"));
|
||||
} else {
|
||||
this.putValue(NAME, trans.get("RocketActions.VisibilityAct.Hide"));
|
||||
this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.VisibilityAct.ttip.Hide"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
var components = new ArrayList<>(selectionModel.getSelectedComponents());
|
||||
|
||||
if (!components.isEmpty()) {
|
||||
var visibility = !components.get(0).isVisible();
|
||||
|
||||
for (var component : components) {
|
||||
if (isRocketOrStage(component)) {
|
||||
getAllDescendants(components).forEach(c -> c.setVisible(visibility));
|
||||
continue;
|
||||
}
|
||||
component.setVisible(visibility);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRocketOrStage(RocketComponent component) {
|
||||
return component instanceof AxialStage || component instanceof Rocket;
|
||||
}
|
||||
|
||||
private Set<RocketComponent> getAllDescendants(List<RocketComponent> components) {
|
||||
var result = new LinkedHashSet<RocketComponent>();
|
||||
var queue = new ArrayDeque<>(components);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
var node = queue.pop();
|
||||
result.add(node);
|
||||
|
||||
for (var child : node.getChildren()) {
|
||||
if (!result.contains(child)) {
|
||||
queue.add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,23 +3,16 @@ package info.openrocket.swing.gui.main.componenttree;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
@ -35,6 +28,7 @@ import info.openrocket.core.startup.Application;
|
||||
import info.openrocket.core.unit.UnitGroup;
|
||||
import info.openrocket.core.util.ArrayList;
|
||||
import info.openrocket.core.util.TextUtil;
|
||||
import info.openrocket.swing.gui.util.Icons;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ComponentTreeRenderer extends DefaultTreeCellRenderer {
|
||||
@ -45,6 +39,7 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer {
|
||||
private static Color textSelectionForegroundColor;
|
||||
private static Color componentTreeBackgroundColor;
|
||||
private static Color componentTreeForegroundColor;
|
||||
private static Color visibilityHiddenForegroundColor;
|
||||
private static Icon massOverrideSubcomponentIcon;
|
||||
private static Icon massOverrideIcon;
|
||||
private static Icon CGOverrideSubcomponentIcon;
|
||||
@ -92,6 +87,11 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer {
|
||||
RocketComponent c = (RocketComponent) value;
|
||||
applyToolTipText(components, c, panel);
|
||||
|
||||
// Set the cell text color if component is hidden
|
||||
if (!c.isVisible() && !sel) {
|
||||
label.setForeground(visibilityHiddenForegroundColor);
|
||||
}
|
||||
|
||||
// Set the tree icon
|
||||
final Icon treeIcon;
|
||||
if (c.getClass().isAssignableFrom(MassComponent.class)) {
|
||||
@ -103,10 +103,11 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer {
|
||||
|
||||
panel.add(new JLabel(treeIcon), BorderLayout.WEST);
|
||||
|
||||
// Add mass/CG/CD overridden icons
|
||||
// Add mass/CG/CD overridden and component hidden icons
|
||||
if (c.isMassOverridden() || c.getMassOverriddenBy() != null ||
|
||||
c.isCGOverridden() || c.getCGOverriddenBy() != null ||
|
||||
c.isCDOverridden() || c.getCDOverriddenBy() != null) {
|
||||
c.isCDOverridden() || c.getCDOverriddenBy() != null ||
|
||||
!c.isVisible()) {
|
||||
List<Icon> icons = new LinkedList<>();
|
||||
if (c.getMassOverriddenBy() != null) {
|
||||
icons.add(massOverrideSubcomponentIcon);
|
||||
@ -123,6 +124,9 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer {
|
||||
} else if (c.isCDOverridden()) {
|
||||
icons.add(CDOverrideIcon);
|
||||
}
|
||||
if (!c.isVisible()) {
|
||||
icons.add(Icons.COMPONENT_HIDDEN);
|
||||
}
|
||||
|
||||
Icon combinedIcon = combineIcons(3, icons.toArray(new Icon[0]));
|
||||
JLabel overrideIconsLabel = new JLabel(combinedIcon);
|
||||
@ -144,6 +148,7 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer {
|
||||
CGOverrideIcon = GUIUtil.getUITheme().getCGOverrideIcon();
|
||||
CDOverrideSubcomponentIcon = GUIUtil.getUITheme().getCDOverrideSubcomponentIcon();
|
||||
CDOverrideIcon = GUIUtil.getUITheme().getCDOverrideIcon();
|
||||
visibilityHiddenForegroundColor = GUIUtil.getUITheme().getvisibilityHiddenForegroundColor();
|
||||
}
|
||||
|
||||
private void applyToolTipText(List<RocketComponent> components, RocketComponent c, JComponent comp) {
|
||||
|
@ -265,6 +265,10 @@ public class RocketFigure extends AbstractScaleFigure {
|
||||
while (!figureShapesCopy.isEmpty()) {
|
||||
RocketComponentShapes rcs = figureShapesCopy.poll();
|
||||
RocketComponent c = rcs.getComponent();
|
||||
|
||||
if (!c.isVisible()) {
|
||||
continue;
|
||||
}
|
||||
boolean selected = false;
|
||||
|
||||
// Check if component is in the selection
|
||||
|
Loading…
x
Reference in New Issue
Block a user