From 6c750e68bd6104589816d195cb802d8774230674 Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Wed, 17 Jul 2024 01:15:47 +1000 Subject: [PATCH 01/13] [#2485] Add icons for component visibility --- .../openrocket/swing/gui/theme/UITheme.java | 22 ++++++++++++++++++ .../info/openrocket/swing/gui/util/Icons.java | 4 ++++ .../resources/pix/icons/component-hidden.png | Bin 0 -> 1558 bytes .../pix/icons/component-hidden_dark.png | Bin 0 -> 1553 bytes .../pix/icons/component-hidden_light.png | Bin 0 -> 364 bytes 5 files changed, 26 insertions(+) create mode 100644 swing/src/main/resources/pix/icons/component-hidden.png create mode 100644 swing/src/main/resources/pix/icons/component-hidden_dark.png create mode 100644 swing/src/main/resources/pix/icons/component-hidden_light.png diff --git a/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java b/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java index 46f523dbf..a7a18e7c6 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java +++ b/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java @@ -99,6 +99,8 @@ public class UITheme { Icon getCDOverrideIcon(); Icon getCDOverrideSubcomponentIcon(); + Icon getVisibilityHiddenIcon(); + Border getBorder(); Border getMarginBorder(); Border getUnitSelectorBorder(); @@ -424,6 +426,11 @@ public class UITheme { return Icons.CD_OVERRIDE_SUBCOMPONENT_LIGHT; } + @Override + public Icon getVisibilityHiddenIcon() { + return Icons.COMPONENT_HIDDEN_LIGHT; + } + @Override public Border getBorder() { return new FlatBorder(); @@ -810,6 +817,11 @@ public class UITheme { return Icons.CD_OVERRIDE_SUBCOMPONENT_DARK; } + @Override + public Icon getVisibilityHiddenIcon() { + return Icons.COMPONENT_HIDDEN_DARK; + } + @Override public Border getBorder() { return new FlatBorder(); @@ -1196,6 +1208,11 @@ public class UITheme { return Icons.CD_OVERRIDE_SUBCOMPONENT_DARK; } + @Override + public Icon getVisibilityHiddenIcon() { + return Icons.COMPONENT_HIDDEN_DARK; + } + @Override public Border getBorder() { return new FlatBorder(); @@ -1601,6 +1618,11 @@ public class UITheme { return getCurrentTheme().getCDOverrideSubcomponentIcon(); } + @Override + public Icon getVisibilityHiddenIcon() { + return getCurrentTheme().getVisibilityHiddenIcon(); + } + @Override public Border getBorder() { return getCurrentTheme().getBorder(); diff --git a/swing/src/main/java/info/openrocket/swing/gui/util/Icons.java b/swing/src/main/java/info/openrocket/swing/gui/util/Icons.java index cf3c6fd75..0f9550811 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/util/Icons.java +++ b/swing/src/main/java/info/openrocket/swing/gui/util/Icons.java @@ -117,6 +117,10 @@ public class Icons { public static final Icon CD_OVERRIDE_SUBCOMPONENT_LIGHT = loadImageIcon("pix/icons/cd-override-subcomponent_light.png", "CD Override Subcomponent"); public static final Icon CD_OVERRIDE_SUBCOMPONENT_DARK = loadImageIcon("pix/icons/cd-override-subcomponent_dark.png", "CD Override Subcomponent"); + public static final Icon COMPONENT_HIDDEN = loadImageIcon("pix/icons/component-hidden.png", "Component Hidden"); + public static final Icon COMPONENT_HIDDEN_DARK = loadImageIcon("pix/icons/component-hidden_dark.png", "Component Hidden"); + public static final Icon COMPONENT_HIDDEN_LIGHT = loadImageIcon("pix/icons/component-hidden_light.png", "Component Hidden"); + // MANUFACTURERS ICONS public static final Icon RASAERO = loadImageIcon("pix/icons/RASAero_16.png", "RASAero Icon"); public static final Icon ROCKSIM = loadImageIcon("pix/icons/Rocksim_16.png", "Rocksim Icon"); diff --git a/swing/src/main/resources/pix/icons/component-hidden.png b/swing/src/main/resources/pix/icons/component-hidden.png new file mode 100644 index 0000000000000000000000000000000000000000..a9df29fa64ccc9d8aca114f1e07103e4914f5159 GIT binary patch literal 1558 zcmV+x2I={UP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=KHlH4E+MgLhvmVhLL#Bwm0s_Y=k&jmlGduH;g zHxxsN@)un&*;p#3z+hxMJe*_>|RCOshXn;x&1)hx-D< zBpBs5wR-wj*!As(YYp0-?ZMA0>}Q}W@)=O(%8W^WOcHiJ?3B(xWj`jlxIL!2@iDg3 zaiww|vfaUs5$EC2kPIqy#X`co3rSqPA3idVBo(8fCsFvJ$DG9ih9u?81cE$0-YsB! z6zEChYZl(Cf6v~D&-Jv+J1z665hkBrApKnYCGo?;a%aTP7mTh@A4MLc`xyK2bTwrt zn$L<_V$&4{j?h2Jx-Fv_4#nJ7D`*~TY(U;)iyK;_LJTS+P@+zi2JLHX)R1E0oed*% zV_7yAQ{N(pzoG;we>%Qhp*mS8D&nYBC_;r$E41oha_JW|X-HCB90k{cgCC1k2N!2u9b5%L@B_rz!Aa3Y zO8j3^Xc6PVaX;SOd)&PP{LLy;&7K6HYL<~nC52pmRS3Nzgb0EdKtf`sJ|~K4c#f}o z`1pDk=ULw8{v7>E!DN6>Af9EqVG(Z-Pj6Z}=Y8TRD@zLTIq|qb7bJeH9AV~VO#zCY)(!g-6cTCKD8J^2g61#M-S>oiA@!XlO+ zLV%1qDyYIjoK}q#6B*i1diaMOzep~bToo{KET9GzlH&*egWuhn#mNabDVzj?FSh+L z3WRonR>QWxk8Qhk0tB9cE3MMDH$92^2;CCXm+cz17iZ~vZY_xA%??{b}x2is`? z000JJOGiWi{{a60|De66lK=n!32;bRa{vGUb^rhXb^%U%CI|ok00(qQO+^Rj2M+}s zF(RY(LI3~(AW1|)R5;6}(=jW>VH5`N-{q!I;tMDfqbwHc#%B;&#bS{xr2LUZDP>R? zOvH*+ln0b^*x1v=w>1V_0*1uKP?kFk#oui-pfsF}ui88y_=TF7G|$X9s5O+lc6S_|&5 zl|0ljP$17W>0kr5c?Mae+`xP?^9+U}{k4RA5Zn4*V2&53I&bDBgh} zpGSxz=nL^`baaU4l#mMK7dDk2$#0Mf>|wG=+P@YU{L8QU0^lh>B`aUp3;+NC07*qo IM6N<$f|onaMF0Q* literal 0 HcmV?d00001 diff --git a/swing/src/main/resources/pix/icons/component-hidden_dark.png b/swing/src/main/resources/pix/icons/component-hidden_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..b7258cff0054af9c82973040aa7b2f3e1ed9d566 GIT binary patch literal 1553 zcmV+s2JZQZP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=KJlI$i7h2L34mVhLL#Bwk{RoOw7p96M#x@RWI zy>;(Ps@jwR3t7nL19~Qmzy6%z4_s6-hot7YmfcmFZOT` zFie70&PS`4e}!E?UUCZ{R&ZnKy6{w7}lZ)GPst2EK zyBv2a*Dc2#>}>HIo(;*MQddkQ%tJ`xuKo0pbtkE44K<0v4;iDc;sFgyxiWzuFQ0b{ z7@q}t5&6*zpVhy6pTy^S+2xa#`O*lTZwE-f7k^9qG_l+n@#}!eBh*)s=j=Y`IKQr@ z3`O&`qPE!d2m?pxpJcz5Q4F`DZ@Ux}k2N+R@3F-LrBNXUl^G~er%IE?ZW}eEn0T}ivw2U5kz3-I~UsXmOWo1$IPAJ%7kEqSN^ete;fRn66S16 zB8q-%g}itzQKYfRtr}hcLi6ILTi{2z@W-e1vzBUt+mhMwfi)gyh(qaPZOP^=@Kdzs z7NTJN3IK^Pw;~x55QrIjfHFm6HX`Wg0927V_6Z*#K&sr4BqtG^N5;-;ZoFf(eOWp4 zGUIFl2$hVz5!b0~u$OWML%IzC!ef`e0P3+u-OVcRWeekL50e6h3ezkoEdIx$3dIx$3dIx$3 z`u_)#i9a6jPht2QnggAu@@$&b0004nX+uL$Nkc;*aB^>EX>4Tx0C=2zkv&MmKpe$i zQ>7x+4t5Z6$WR@`f~csYR-p(LLaorMgUO{|(4-+rad8w}3l4rPRvlcNb#-tR1i=pw zX9p)m7b)?7Nufoo2gm(*ckglc4iIW3rdb_hfTr7KG9DAtnN>0HiU4{M!Zb!?W*Kvm zltA0@bq^n3@1i`b{kcC!kD9d@;1h{wnPJ+*8^lwaw!wLyILr#NN_mbp%I z5OFMG2@*sosGx`vY(#0*NwJWm{a73Sfa{mYrI4!#MveuPp+R>2;D7MDTPrs)?j;3d zK5#ex~VC9z~v6m|76If z>`H!`LM{iqpV2pEfWBLxd(G>uv5(USAVpmzZh(VBU^q|N>uuiM)!N&?XBz$e0Kj5$ z#(11?z5oCK24YJ`L;(K){{a7>y{D4^000SaNLh0L004FX004FYPJ1Q@00007bV*G` z2j~Y54>}zw&u#Gl009+AL_t(I%caw?PLx3u#qqO3LnS75Bs>HqiOCmj40cvqLu0uV zmIeuy6c#ijv^;{n2{p-9KEQUEKr}AIV%eX~teKsq5cejxn9Tgoz4v_g>mkd;lPnqk zE6^nwCP~odCXO(~PmJ*mpRtA)xWY@!3H0zDcNXIwd+2bBof%idXBF@p`*@0VY!`T3 z?k`F=e+51@75DK5BfKr}ujbA9OrXQGxqMdO-Kui`fze*ACYdCeCRwRm?j;!{xk>UR z$#s&i&6kj5G8^HrF>JdsY=qbN&{Y4}ns{7Bc~{Nc$8#KDQo*N%S=fWE#_cx77}QKF z(BV5yszv&N%hK{jyLk0sZo~dH>%*{gkjG?i+28pO_|0k+2?- zG7x6;t5)LxG9*h}BT9nv(@M${i&7cN%ggmL^RkPR6AM!H@{7`Ezq647DthVZ;uvDl zd-al`esiM8(U0>t%$RUP#jYh#DKnzw?3I~ES2(*bOf-?u5z4lAD({TEbdr6;M1eNWp@6tt-~+DXGmZ%5ZJ2Y=(BxxJomKt~ zuIF;s+zzIzK49hbNtEhl6gS-8VKU#!r;m5dV<~Ay`=!=zKRMmIT%h*g?4;sam#tGd z_Z(w>yz&y?yNd^9&jr7&+pu7&nAwXv-72eZ_`dsH+37wpW%rexeUWckZ!_5OKASDZ zlW#p=X;tW|w&*!OetGck=s8hre{ib(o9(aMn)Y91m%JXve$hN!4CrYFPgg&ebxsLQ E0H2PLv;Y7A literal 0 HcmV?d00001 From 32beec501f520a925d031d7f6366765096398e67 Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Wed, 17 Jul 2024 01:34:47 +1000 Subject: [PATCH 02/13] [#2485] Add English translation --- core/src/main/resources/l10n/messages.properties | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/resources/l10n/messages.properties b/core/src/main/resources/l10n/messages.properties index cc5be690d..42c08cc0a 100644 --- a/core/src/main/resources/l10n/messages.properties +++ b/core/src/main/resources/l10n/messages.properties @@ -49,6 +49,14 @@ RocketActions.MoveDownAct.Movedown = Move down RocketActions.MoveDownAct.ttip.Movedown = Move this component downwards. RocketActions.ExportOBJAct.ExportOBJ = Export as OBJ (.obj) RocketActions.ExportOBJAct.ttip.ExportOBJ = Export the selected components as a Wavefront OBJ 3D file. +RocketActions.VisibilityAct.Hide = Hide +RocketActions.VisibilityAct.ttip.Hide = Hide the selected component. +RocketActions.VisibilityAct.Show = Show +RocketActions.VisibilityAct.ttip.Show = Show the selected component. +RocketActions.VisibilityAct.ShowAll = Show all +RocketActions.VisibilityAct.ttip.ShowAll = Show all components. +RocketActions.VisibilityAct.HideAll = Hide all +RocketActions.VisibilityAct.ttip.HideAll = Hide all components. ! RocketPanel RocketPanel.FigTypeAct.SideView = Side view From b37d694a34d55a52902c96b908551448e8877352 Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Wed, 17 Jul 2024 01:35:47 +1000 Subject: [PATCH 03/13] [#2485] Add UI colors --- .../openrocket/swing/gui/theme/UITheme.java | 21 +++++++++++++++++++ .../main/resources/themes/FlatLaf.properties | 2 ++ 2 files changed, 23 insertions(+) diff --git a/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java b/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java index a7a18e7c6..be237e042 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java +++ b/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java @@ -85,6 +85,7 @@ public class UITheme { Color getComponentTreeBackgroundColor(); Color getComponentTreeForegroundColor(); + Color getvisibilityHiddenForegroundColor(); Color getFinPointGridMajorLineColor(); Color getFinPointGridMinorLineColor(); @@ -371,6 +372,11 @@ public class UITheme { return UIManager.getColor("Tree.textForeground"); } + @Override + public Color getvisibilityHiddenForegroundColor() { + return UIManager.getColor("Tree.textForeground.hidden.light"); + } + @Override public Color getFinPointGridMajorLineColor() { return new Color( 0, 0, 255, 80); @@ -762,6 +768,11 @@ public class UITheme { return getTextColor(); } + @Override + public Color getvisibilityHiddenForegroundColor() { + return UIManager.getColor("Tree.textForeground.hidden.dark"); + } + @Override public Color getFinPointGridMajorLineColor() { return new Color(135, 135, 199, 197); @@ -1153,6 +1164,11 @@ public class UITheme { return getTextColor(); } + @Override + public Color getvisibilityHiddenForegroundColor() { + return UIManager.getColor("Tree.textForeground.hidden.dark"); + } + @Override public Color getFinPointGridMajorLineColor() { return new Color(164, 164, 224, 197); @@ -1563,6 +1579,11 @@ public class UITheme { return getCurrentTheme().getComponentTreeForegroundColor(); } + @Override + public Color getvisibilityHiddenForegroundColor() { + return getCurrentTheme().getvisibilityHiddenForegroundColor(); + } + @Override public Color getFinPointGridMajorLineColor() { return getCurrentTheme().getFinPointGridMajorLineColor(); diff --git a/swing/src/main/resources/themes/FlatLaf.properties b/swing/src/main/resources/themes/FlatLaf.properties index a9f98b247..0187ded38 100644 --- a/swing/src/main/resources/themes/FlatLaf.properties +++ b/swing/src/main/resources/themes/FlatLaf.properties @@ -9,6 +9,8 @@ Tree.paintLines = true Tree.showCellFocusIndicator = true Tree.wideSelection = true Tree.paintSelection = true +Tree.textForeground.hidden.dark = darken($Tree.textForeground, 25%) +Tree.textForeground.hidden.light = lighten($Tree.textForeground, 25%) # Margin as top,left,bottom,right Tree.rendererMargins = 2, 0, 3, 0 From 7d71a9367e74c50df91ff694f16f77b9018e39ab Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Wed, 17 Jul 2024 01:39:49 +1000 Subject: [PATCH 04/13] [#2485] Add component visibility functionality --- .../core/rocketcomponent/RocketComponent.java | 24 +++- .../figure3d/geometry/ComponentRenderer.java | 3 + .../openrocket/swing/gui/main/BasicFrame.java | 7 +- .../swing/gui/main/RocketActions.java | 103 ++++++++++++++++-- .../componenttree/ComponentTreeRenderer.java | 23 ++-- .../swing/gui/scalefigure/RocketFigure.java | 4 + 6 files changed, 141 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/info/openrocket/core/rocketcomponent/RocketComponent.java b/core/src/main/java/info/openrocket/core/rocketcomponent/RocketComponent.java index b4e44cac7..f3b86d98e 100644 --- a/core/src/main/java/info/openrocket/core/rocketcomponent/RocketComponent.java +++ b/core/src/main/java/info/openrocket/core/rocketcomponent/RocketComponent.java @@ -146,6 +146,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; /** @@ -2706,7 +2712,23 @@ 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 ////////// diff --git a/swing/src/main/java/info/openrocket/swing/gui/figure3d/geometry/ComponentRenderer.java b/swing/src/main/java/info/openrocket/swing/gui/figure3d/geometry/ComponentRenderer.java index 6b51773d4..b04a76396 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/figure3d/geometry/ComponentRenderer.java +++ b/swing/src/main/java/info/openrocket/swing/gui/figure3d/geometry/ComponentRenderer.java @@ -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) { diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java b/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java index 686277cee..a32340323 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java @@ -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")); diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java index 3e17f4e31..f5eee4550 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java @@ -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 getAllDescendants(List components) { + var result = new LinkedHashSet(); + 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; + } + } } diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/componenttree/ComponentTreeRenderer.java b/swing/src/main/java/info/openrocket/swing/gui/main/componenttree/ComponentTreeRenderer.java index 0d4ea5ba6..e36086901 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/componenttree/ComponentTreeRenderer.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/componenttree/ComponentTreeRenderer.java @@ -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 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 components, RocketComponent c, JComponent comp) { diff --git a/swing/src/main/java/info/openrocket/swing/gui/scalefigure/RocketFigure.java b/swing/src/main/java/info/openrocket/swing/gui/scalefigure/RocketFigure.java index fdcfb9dfa..62bfac6b9 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/scalefigure/RocketFigure.java +++ b/swing/src/main/java/info/openrocket/swing/gui/scalefigure/RocketFigure.java @@ -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 From 3d77c2d8e9cc28b6aae36ac771116c253b6310bb Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Wed, 17 Jul 2024 02:56:19 +1000 Subject: [PATCH 05/13] [#2485] Remove key stroke and mnemonic --- .../main/java/info/openrocket/swing/gui/main/BasicFrame.java | 1 - .../java/info/openrocket/swing/gui/main/RocketActions.java | 4 ---- 2 files changed, 5 deletions(-) diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java b/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java index a32340323..725349274 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java @@ -608,7 +608,6 @@ public class BasicFrame extends JFrame { editMenu.add(item); item = new JMenuItem(actions.getToggleVisibilityAction()); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_COMMA, SHORTCUT_KEY)); editMenu.add(item); diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java index f5eee4550..a9123efd4 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java @@ -54,8 +54,6 @@ 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; @@ -1267,8 +1265,6 @@ public class RocketActions { 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(); } From 2f524016dec2581e5ea2584c4e0206aaffa5903d Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:24:01 +1000 Subject: [PATCH 06/13] [#2485] Fix method name --- .../main/componenttree/ComponentTreeRenderer.java | 2 +- .../info/openrocket/swing/gui/theme/UITheme.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/componenttree/ComponentTreeRenderer.java b/swing/src/main/java/info/openrocket/swing/gui/main/componenttree/ComponentTreeRenderer.java index e36086901..5deaacca2 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/componenttree/ComponentTreeRenderer.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/componenttree/ComponentTreeRenderer.java @@ -148,7 +148,7 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer { CGOverrideIcon = GUIUtil.getUITheme().getCGOverrideIcon(); CDOverrideSubcomponentIcon = GUIUtil.getUITheme().getCDOverrideSubcomponentIcon(); CDOverrideIcon = GUIUtil.getUITheme().getCDOverrideIcon(); - visibilityHiddenForegroundColor = GUIUtil.getUITheme().getvisibilityHiddenForegroundColor(); + visibilityHiddenForegroundColor = GUIUtil.getUITheme().getVisibilityHiddenForegroundColor(); } private void applyToolTipText(List components, RocketComponent c, JComponent comp) { diff --git a/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java b/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java index be237e042..7444611ea 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java +++ b/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java @@ -85,7 +85,7 @@ public class UITheme { Color getComponentTreeBackgroundColor(); Color getComponentTreeForegroundColor(); - Color getvisibilityHiddenForegroundColor(); + Color getVisibilityHiddenForegroundColor(); Color getFinPointGridMajorLineColor(); Color getFinPointGridMinorLineColor(); @@ -373,7 +373,7 @@ public class UITheme { } @Override - public Color getvisibilityHiddenForegroundColor() { + public Color getVisibilityHiddenForegroundColor() { return UIManager.getColor("Tree.textForeground.hidden.light"); } @@ -769,7 +769,7 @@ public class UITheme { } @Override - public Color getvisibilityHiddenForegroundColor() { + public Color getVisibilityHiddenForegroundColor() { return UIManager.getColor("Tree.textForeground.hidden.dark"); } @@ -1165,7 +1165,7 @@ public class UITheme { } @Override - public Color getvisibilityHiddenForegroundColor() { + public Color getVisibilityHiddenForegroundColor() { return UIManager.getColor("Tree.textForeground.hidden.dark"); } @@ -1580,8 +1580,8 @@ public class UITheme { } @Override - public Color getvisibilityHiddenForegroundColor() { - return getCurrentTheme().getvisibilityHiddenForegroundColor(); + public Color getVisibilityHiddenForegroundColor() { + return getCurrentTheme().getVisibilityHiddenForegroundColor(); } @Override From 7751faccdabbeb87f3bc73efc9e5ab0704baa196 Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Wed, 17 Jul 2024 22:30:29 +1000 Subject: [PATCH 07/13] [#2485] Fix motor mount not hiding --- .../info/openrocket/swing/gui/figure3d/RocketRenderer.java | 5 ++++- .../info/openrocket/swing/gui/scalefigure/RocketFigure.java | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/swing/src/main/java/info/openrocket/swing/gui/figure3d/RocketRenderer.java b/swing/src/main/java/info/openrocket/swing/gui/figure3d/RocketRenderer.java index 8d24294ac..17524e7a6 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/figure3d/RocketRenderer.java +++ b/swing/src/main/java/info/openrocket/swing/gui/figure3d/RocketRenderer.java @@ -214,7 +214,10 @@ public abstract class RocketRenderer { if( null == motor ){ throw new NullPointerException(" null motor from configuration.getActiveMotors... this is a bug."); } - + + if (!((RocketComponent) mount).isVisible()) { + continue; + } double length = motor.getLength(); Coordinate[] position = ((RocketComponent) mount).toAbsolute(new Coordinate(((RocketComponent) mount) diff --git a/swing/src/main/java/info/openrocket/swing/gui/scalefigure/RocketFigure.java b/swing/src/main/java/info/openrocket/swing/gui/scalefigure/RocketFigure.java index 62bfac6b9..cf58cbc97 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/scalefigure/RocketFigure.java +++ b/swing/src/main/java/info/openrocket/swing/gui/scalefigure/RocketFigure.java @@ -326,6 +326,10 @@ public class RocketFigure extends AbstractScaleFigure { double motorRadius = motor.getDiameter() / 2; RocketComponent mountComponent = ((RocketComponent) mount); + if (!mountComponent.isVisible()) { + continue; + } + // .getLocation() will return all the parent instances of this owning component, AND all of it's own instances as well. // so, just draw a motor once for each Coordinate returned... Coordinate[] mountLocations = mount.getLocations(); From 3f3352302fe745600cef14bec7b24d60595efdb1 Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Fri, 19 Jul 2024 09:52:56 +1000 Subject: [PATCH 08/13] [#2485] Fix visibility toggling logic and feedback --- .../core/rocketcomponent/RocketComponent.java | 2 + .../main/resources/l10n/messages.properties | 9 +- .../swing/gui/main/RocketActions.java | 117 +++++++++++------- 3 files changed, 81 insertions(+), 47 deletions(-) diff --git a/core/src/main/java/info/openrocket/core/rocketcomponent/RocketComponent.java b/core/src/main/java/info/openrocket/core/rocketcomponent/RocketComponent.java index f3b86d98e..09b72676c 100644 --- a/core/src/main/java/info/openrocket/core/rocketcomponent/RocketComponent.java +++ b/core/src/main/java/info/openrocket/core/rocketcomponent/RocketComponent.java @@ -2716,6 +2716,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab /** * Returns true if this component is visible. * @return True if this component is visible. + * @apiNote The component is rendered if true is returned. */ public boolean isVisible() { return isVisible; @@ -2724,6 +2725,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab /** * Sets the component's visibility to the specified value. * @param value Visibility value + * @apiNote The component is rendered if the specified value is set to true. */ public void setVisible(boolean value) { this.isVisible = value; diff --git a/core/src/main/resources/l10n/messages.properties b/core/src/main/resources/l10n/messages.properties index 36b7c2b8f..c32cba6a5 100644 --- a/core/src/main/resources/l10n/messages.properties +++ b/core/src/main/resources/l10n/messages.properties @@ -49,14 +49,15 @@ RocketActions.MoveDownAct.Movedown = Move down RocketActions.MoveDownAct.ttip.Movedown = Move this component downwards. RocketActions.ExportOBJAct.ExportOBJ = Export as OBJ (.obj) RocketActions.ExportOBJAct.ttip.ExportOBJ = Export the selected components as a Wavefront OBJ 3D file. -RocketActions.VisibilityAct.Hide = Hide -RocketActions.VisibilityAct.ttip.Hide = Hide the selected component. -RocketActions.VisibilityAct.Show = Show -RocketActions.VisibilityAct.ttip.Show = Show the selected component. + RocketActions.VisibilityAct.ShowAll = Show all RocketActions.VisibilityAct.ttip.ShowAll = Show all components. RocketActions.VisibilityAct.HideAll = Hide all RocketActions.VisibilityAct.ttip.HideAll = Hide all components. +RocketActions.VisibilityAct.ShowSelected = Show selected +RocketActions.VisibilityAct.ttip.ShowSelected = Show selected components. +RocketActions.VisibilityAct.HideSelected = Hide selected +RocketActions.VisibilityAct.ttip.HideSelected = Hide selected components. ! RocketPanel RocketPanel.FigTypeAct.SideView = Side view diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java index a9123efd4..16cf900f7 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java @@ -1259,40 +1259,40 @@ public class RocketActions { /** * Action to toggle the visibility of the selected components. + * @see RocketComponent#isVisible() */ 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()); + super.putValue(NAME, trans.get("RocketActions.VisibilityAct.HideSelected")); + super.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.VisibilityAct.ttip.HideSelected")); + super.putValue(SMALL_ICON, GUIUtil.getUITheme().getVisibilityHiddenIcon()); 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.isEmpty()) { + return; + } + super.setEnabled(true); - 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")); - } - } + if (isRocketSelected(components)) { + super.putValue(NAME, rocket.isVisible() ? + trans.get("RocketActions.VisibilityAct.HideAll") : + trans.get("RocketActions.VisibilityAct.ShowAll")); + super.putValue(SHORT_DESCRIPTION, rocket.isVisible() ? + trans.get("RocketActions.VisibilityAct.ttip.HideAll") : + trans.get("RocketActions.VisibilityAct.ttip.ShowAll")); + } else { + var visibility = components.stream().anyMatch(RocketComponent::isVisible); + super.putValue(NAME, visibility ? + trans.get("RocketActions.VisibilityAct.HideSelected") : + trans.get("RocketActions.VisibilityAct.ShowSelected")); + super.putValue(SHORT_DESCRIPTION, visibility ? + trans.get("RocketActions.VisibilityAct.ttip.HideSelected") : + trans.get("RocketActions.VisibilityAct.ttip.ShowSelected")); } } @@ -1300,36 +1300,67 @@ public class RocketActions { 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); - } + if (components.isEmpty()) { + return; } + + // Toggle the visibility of the rocket and its descendants + if (isRocketSelected(components)) { + var rocketVisibility = !rocket.isVisible(); + rocket.setVisible(rocketVisibility); + getDescendants(rocket).forEach(descendant -> descendant.setVisible(rocketVisibility)); + return; + } + var visibility = components.stream().noneMatch(RocketComponent::isVisible); + + // Toggle the visibility of all non-stage and non-rocket components + components.stream().filter(c -> !(c instanceof AxialStage || c instanceof Rocket)).forEach(component -> { + component.setVisible(visibility); + + // Update the visibility of this component's stage + var stage = component.getStage(); + stage.setVisible(getDescendants(stage).stream().anyMatch(RocketComponent::isVisible)); + + // Update the visibility of the rocket + rocket.setVisible(getDescendants(rocket).stream().anyMatch(RocketComponent::isVisible)); + }); + + // Toggle the visibility of all stage components and their descendants + components.stream().filter(AxialStage.class::isInstance).forEach(stage -> { + stage.setVisible(visibility); + getDescendants(stage).forEach(descendant -> descendant.setVisible(visibility)); + }); } - private boolean isRocketOrStage(RocketComponent component) { - return component instanceof AxialStage || component instanceof Rocket; + /** + * Returns true if the rocket or all descendant are in the specified list. + * + * @param components Components to query + * @return True if all components are selected + */ + private boolean isRocketSelected(List components) { + var rocketSelected = components.stream().anyMatch(Rocket.class::isInstance); + var allComponentsSelected = getDescendants(rocket).size() == components.size(); + return rocketSelected || allComponentsSelected; } - private Set getAllDescendants(List components) { + /** + * Returns all descendants of the specified component. + * + * @param component Component to query + * @return All descendants + * @apiNote Returns an empty set if the component does not have children. + */ + private Set getDescendants(RocketComponent component) { + Objects.requireNonNull(component); + var result = new LinkedHashSet(); - var queue = new ArrayDeque<>(components); + var queue = new ArrayDeque<>(component.getChildren()); while (!queue.isEmpty()) { var node = queue.pop(); result.add(node); - - for (var child : node.getChildren()) { - if (!result.contains(child)) { - queue.add(child); - } - } + node.getChildren().stream().filter(c -> !result.contains(c)).forEach(queue::add); } return result; } From a9456be0fb0221f1db1076ce94070681e6553c27 Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Fri, 19 Jul 2024 12:31:42 +1000 Subject: [PATCH 09/13] [#2485] Add show all action --- .../main/resources/l10n/messages.properties | 1 + .../openrocket/swing/gui/main/BasicFrame.java | 8 ++- .../swing/gui/main/RocketActions.java | 60 ++++++++++++------ .../openrocket/swing/gui/theme/UITheme.java | 21 ++++++ .../info/openrocket/swing/gui/util/Icons.java | 2 + .../pix/icons/component-showing_dark.png | Bin 0 -> 1524 bytes .../pix/icons/component-showing_light.png | Bin 0 -> 306 bytes 7 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 swing/src/main/resources/pix/icons/component-showing_dark.png create mode 100644 swing/src/main/resources/pix/icons/component-showing_light.png diff --git a/core/src/main/resources/l10n/messages.properties b/core/src/main/resources/l10n/messages.properties index c32cba6a5..679525eac 100644 --- a/core/src/main/resources/l10n/messages.properties +++ b/core/src/main/resources/l10n/messages.properties @@ -50,6 +50,7 @@ RocketActions.MoveDownAct.ttip.Movedown = Move this component downwards. RocketActions.ExportOBJAct.ExportOBJ = Export as OBJ (.obj) RocketActions.ExportOBJAct.ttip.ExportOBJ = Export the selected components as a Wavefront OBJ 3D file. +RocketActions.Visibility = Visibility RocketActions.VisibilityAct.ShowAll = Show all RocketActions.VisibilityAct.ttip.ShowAll = Show all components. RocketActions.VisibilityAct.HideAll = Hide all diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java b/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java index 725349274..fa6306cae 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/BasicFrame.java @@ -607,9 +607,15 @@ public class BasicFrame extends JFrame { item = new JMenuItem(actions.getScaleAction()); editMenu.add(item); + //// Visibility + JMenu visibilitySubMenu = new JMenu(trans.get("RocketActions.Visibility")); + editMenu.add(visibilitySubMenu); item = new JMenuItem(actions.getToggleVisibilityAction()); - editMenu.add(item); + visibilitySubMenu.add(item); + item = new JMenuItem(actions.getShowAllComponentsAction()); + visibilitySubMenu.add(item); + editMenu.addSeparator(); //// Preferences item = new JMenuItem(trans.get("main.menu.edit.preferences")); diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java index 16cf900f7..49cb69123 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java @@ -75,6 +75,7 @@ public class RocketActions { private final RocketAction moveDownAction; private final RocketAction exportOBJAction; private final RocketAction toggleVisibilityAction; + private final RocketAction showAllComponentsAction; private static final Translator trans = Application.getTranslator(); private static final Logger log = LoggerFactory.getLogger(RocketActions.class); @@ -101,6 +102,7 @@ public class RocketActions { this.moveDownAction = new MoveDownAction(); this.exportOBJAction = new ExportOBJAction(); this.toggleVisibilityAction = new ToggleVisibilityAction(); + this.showAllComponentsAction = new ShowAllComponentsAction(); OpenRocketClipboard.addClipboardListener(new ClipboardListener() { @Override @@ -150,6 +152,7 @@ public class RocketActions { moveDownAction.clipboardChanged(); exportOBJAction.clipboardChanged(); toggleVisibilityAction.clipboardChanged(); + showAllComponentsAction.clipboardChanged(); } @@ -207,6 +210,10 @@ public class RocketActions { return toggleVisibilityAction; } + public Action getShowAllComponentsAction() { + return showAllComponentsAction; + } + /** * Tie an action to a JButton, without using the icon or text of the action for the button. * @@ -482,7 +489,26 @@ public class RocketActions { return result; } - + /** + * Returns all descendants of the specified component. + * + * @param component Component to query + * @return All descendants + * @apiNote Returns an empty set if the component does not have children. + */ + private Set getDescendants(RocketComponent component) { + Objects.requireNonNull(component); + + var result = new LinkedHashSet(); + var queue = new ArrayDeque<>(component.getChildren()); + + while (!queue.isEmpty()) { + var node = queue.pop(); + result.add(node); + node.getChildren().stream().filter(c -> !result.contains(c)).forEach(queue::add); + } + return result; + } /////// Action classes @@ -1343,26 +1369,24 @@ public class RocketActions { var allComponentsSelected = getDescendants(rocket).size() == components.size(); return rocketSelected || allComponentsSelected; } + } - /** - * Returns all descendants of the specified component. - * - * @param component Component to query - * @return All descendants - * @apiNote Returns an empty set if the component does not have children. - */ - private Set getDescendants(RocketComponent component) { - Objects.requireNonNull(component); + private class ShowAllComponentsAction extends RocketAction { + public ShowAllComponentsAction() { + super.putValue(NAME, trans.get("RocketActions.VisibilityAct.ShowAll")); + super.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.VisibilityAct.ttip.ShowAll")); + super.putValue(SMALL_ICON, GUIUtil.getUITheme().getVisibilityShowingIcon()); + clipboardChanged(); + } - var result = new LinkedHashSet(); - var queue = new ArrayDeque<>(component.getChildren()); + @Override + public void clipboardChanged() { + super.setEnabled(getDescendants(rocket).stream().anyMatch(c -> !c.isVisible())); + } - while (!queue.isEmpty()) { - var node = queue.pop(); - result.add(node); - node.getChildren().stream().filter(c -> !result.contains(c)).forEach(queue::add); - } - return result; + @Override + public void actionPerformed(ActionEvent e) { + getDescendants(rocket).forEach(descendant -> descendant.setVisible(true)); } } } diff --git a/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java b/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java index 7444611ea..5be4f6a45 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java +++ b/swing/src/main/java/info/openrocket/swing/gui/theme/UITheme.java @@ -101,6 +101,7 @@ public class UITheme { Icon getCDOverrideSubcomponentIcon(); Icon getVisibilityHiddenIcon(); + Icon getVisibilityShowingIcon(); Border getBorder(); Border getMarginBorder(); @@ -437,6 +438,11 @@ public class UITheme { return Icons.COMPONENT_HIDDEN_LIGHT; } + @Override + public Icon getVisibilityShowingIcon() { + return Icons.COMPONENT_SHOWING_LIGHT; + } + @Override public Border getBorder() { return new FlatBorder(); @@ -833,6 +839,11 @@ public class UITheme { return Icons.COMPONENT_HIDDEN_DARK; } + @Override + public Icon getVisibilityShowingIcon() { + return Icons.COMPONENT_SHOWING_DARK; + } + @Override public Border getBorder() { return new FlatBorder(); @@ -1229,6 +1240,11 @@ public class UITheme { return Icons.COMPONENT_HIDDEN_DARK; } + @Override + public Icon getVisibilityShowingIcon() { + return Icons.COMPONENT_SHOWING_DARK; + } + @Override public Border getBorder() { return new FlatBorder(); @@ -1644,6 +1660,11 @@ public class UITheme { return getCurrentTheme().getVisibilityHiddenIcon(); } + @Override + public Icon getVisibilityShowingIcon() { + return getCurrentTheme().getVisibilityHiddenIcon(); + } + @Override public Border getBorder() { return getCurrentTheme().getBorder(); diff --git a/swing/src/main/java/info/openrocket/swing/gui/util/Icons.java b/swing/src/main/java/info/openrocket/swing/gui/util/Icons.java index 0f9550811..8711c980b 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/util/Icons.java +++ b/swing/src/main/java/info/openrocket/swing/gui/util/Icons.java @@ -120,6 +120,8 @@ public class Icons { public static final Icon COMPONENT_HIDDEN = loadImageIcon("pix/icons/component-hidden.png", "Component Hidden"); public static final Icon COMPONENT_HIDDEN_DARK = loadImageIcon("pix/icons/component-hidden_dark.png", "Component Hidden"); public static final Icon COMPONENT_HIDDEN_LIGHT = loadImageIcon("pix/icons/component-hidden_light.png", "Component Hidden"); + public static final Icon COMPONENT_SHOWING_DARK = loadImageIcon("pix/icons/component-showing_dark.png", "Component Showing"); + public static final Icon COMPONENT_SHOWING_LIGHT = loadImageIcon("pix/icons/component-showing_light.png", "Component Showing"); // MANUFACTURERS ICONS public static final Icon RASAERO = loadImageIcon("pix/icons/RASAero_16.png", "RASAero Icon"); diff --git a/swing/src/main/resources/pix/icons/component-showing_dark.png b/swing/src/main/resources/pix/icons/component-showing_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..7992c961d30063f53f874faf4945c32d85f3ead7 GIT binary patch literal 1524 zcmV zaB^>EX>4U6ba`-PAZ2)IW&i+q+T~Vjmh>nH{qHJf34$WH9Ha4^*}*J-uIAD8>YlF0 zuleFhG=M}Gf{C4H}Bp>Hky zY_D2&`vSRqZ8&1kaxHhZYuI*2hv(g47?Bx^c9|sPyvZrVHUyWvb_ zztgUh-FCdYOF>ki)C7S7^BO4l`Lr#AFzy%%X2p|2h8;mxa0fT114RIG^?4_N{t}=o zkdKwSMgLsgfX`)@hZ`)@ju93Q6(owFkAq6nA?U!#VuIt92vuW@fDOeU;TnnDHDU0?kG~DLY>w*SE@)c^2&;txpJ5? z3(A>m7(B%>sI!I~S}>4Rd37N`%x5k%iHp!(b4eo{U&T<>*2MBYxU zkQa~IMH+*-@q`r!p}zAb6X0X)^!tnYL8*d4nJ^nZu=r(jv24Y^)mE512iOVb?Ff<6 zwmSesh%HEl0vPa#f>0u_%t{0t4FnZrj&s5b7$6aDNRnd+iJh|Xh>bl;)0Tx34>5WZ zh>(zQwhRmvkQJk#el!|ts7R0~B1u%tNtTjAib+yRnnsf=8Z@eCQdQHe#ezi>OQvR) ztz^ytJy3GWnl0yC3cG?p1-=UG3nU9KVTp?@Y0<@&yp)Rasj*5;RjbupOXC(A+C)oD zo3-4^N^V7`SmP>dT6MKGucf2bdg!rBPhGq9+{?+@&g$KGf;HM%d7~J4FgG~!8Qm-hBS{(DxJH99P#9NY>2z}U#oVuWb7H^Z zjsJ@|H>mpra}Lzq^Y(zXHh$;oWNbTy15+Px`k-H}IJPo=)XKZ_|K3qw23}9??RG74 zFT{Ts*1h*lUwOIL;@{8iGx_-8=#}_4Zm`R(KdOK4)V-g}w#z+d{=K#PKvDm3j-G?~ z@`$`i`=NcA{#&hkt^3>`D)*rV`Pk79@xN^{?lbvJM^D86AusoC(|@P={{fCV{-b2~ zFTByhUqdw~FaQ7nglR)VP)S2WAaHVTW@&6?004NLeUUv#!$2IxUsI(b6^9lPamY}e zEQpHerd23Hg-|QB>R@u|htQ-UNpW!$Tni3s_2@ z`Jel9^r(4@0RfSCh8dt?1$8VgAE(<&}YG%@N z#8G0g*v3j5vy!P1PY}mcO{aVz>#@pti?dd)vgSSc3&VMRd5P;Z2a&`A79l}|f+{vp zhK)F_Iw=;?w4dEq|pBOns7GYiY3~pm!U%xNd369&ot>^gkK0DZ5gTrcfvV?`QN) zIUsTibgz27HTQA)0A#4E)D3WO2n-h~d(G$FUG2U7d#2gn4}Vy4tS(!O)Bpeg24YJ` zL;(K){{a7>y{D4^000SaNLh0L004Oa004ObIJJlp00007bV*G`2j~YA0v8n8l9Q_d z005jxL_t(I%f*t-3Bxc9MnCtMTZb@|E|p6lE5y@uiVTn?N~dtoAtA399faHT;y?rf z^Ym{?I$Odo2wWxICEX0YBt1h2@4wlA2t0s=Z&}P1_kvqsbpaEQOee=zpe=(tU~^7` z@djw@JvjRYbiVD`S2o@Q*|az2JDGtfU@T@$RyjMCjithOyj1V8dW7mvJYI apCTW9hpd>-BriY!00002?- zN)TqW*xDfkWJs2{MwA5SrERJ7jH#WBR9 zckSeZoQDl~T<3fH?=0nTei^irWs{SQ@A4B2k&8;WzRL(Lio3FC$3B+O4+m>rdQVej z7d)7(K3&S<`U!zUtUDOPm(Dxj>=I{xQ0Wt^K%?~rhM*dnDQi1HbZ^l&war Date: Fri, 19 Jul 2024 12:36:21 +1000 Subject: [PATCH 10/13] [#2485] Fix toggle action enabled state --- .../main/java/info/openrocket/swing/gui/main/RocketActions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java index 49cb69123..609b96dcd 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java @@ -1298,11 +1298,11 @@ public class RocketActions { @Override public void clipboardChanged() { var components = new ArrayList<>(selectionModel.getSelectedComponents()); + super.setEnabled(!components.isEmpty()); if (components.isEmpty()) { return; } - super.setEnabled(true); if (isRocketSelected(components)) { super.putValue(NAME, rocket.isVisible() ? From 196d533b191636ececb3fe5d67bbce12ef2762ba Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Fri, 19 Jul 2024 12:36:59 +1000 Subject: [PATCH 11/13] [#2485] Add component showing icon --- .../java/info/openrocket/swing/gui/main/RocketActions.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java index 609b96dcd..b7afd27c5 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java @@ -1311,6 +1311,9 @@ public class RocketActions { super.putValue(SHORT_DESCRIPTION, rocket.isVisible() ? trans.get("RocketActions.VisibilityAct.ttip.HideAll") : trans.get("RocketActions.VisibilityAct.ttip.ShowAll")); + super.putValue(SMALL_ICON, rocket.isVisible() ? + GUIUtil.getUITheme().getVisibilityHiddenIcon() : + GUIUtil.getUITheme().getVisibilityShowingIcon()); } else { var visibility = components.stream().anyMatch(RocketComponent::isVisible); super.putValue(NAME, visibility ? @@ -1319,6 +1322,9 @@ public class RocketActions { super.putValue(SHORT_DESCRIPTION, visibility ? trans.get("RocketActions.VisibilityAct.ttip.HideSelected") : trans.get("RocketActions.VisibilityAct.ttip.ShowSelected")); + super.putValue(SMALL_ICON, visibility ? + GUIUtil.getUITheme().getVisibilityHiddenIcon() : + GUIUtil.getUITheme().getVisibilityShowingIcon()); } } From 8c95f6104d953e8d5652ee0ffb2386fc2f70e172 Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Fri, 19 Jul 2024 12:47:19 +1000 Subject: [PATCH 12/13] [#2485] Fix rocket visibility when show all is actioned --- .../main/java/info/openrocket/swing/gui/main/RocketActions.java | 1 + 1 file changed, 1 insertion(+) diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java index b7afd27c5..df914e21e 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java @@ -1392,6 +1392,7 @@ public class RocketActions { @Override public void actionPerformed(ActionEvent e) { + rocket.setVisible(true); getDescendants(rocket).forEach(descendant -> descendant.setVisible(true)); } } From bee30d69e79b1c6907a986e34df88509efff99c8 Mon Sep 17 00:00:00 2001 From: Ahanu Dewhirst <16126069+Orcjett@users.noreply.github.com> Date: Fri, 19 Jul 2024 12:58:59 +1000 Subject: [PATCH 13/13] [#2485] Add code comments --- .../java/info/openrocket/swing/gui/main/RocketActions.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java index df914e21e..418b5ff3e 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java +++ b/swing/src/main/java/info/openrocket/swing/gui/main/RocketActions.java @@ -1377,6 +1377,10 @@ public class RocketActions { } } + /** + * Action to show all hidden components. + * @see RocketComponent#isVisible() + */ private class ShowAllComponentsAction extends RocketAction { public ShowAllComponentsAction() { super.putValue(NAME, trans.get("RocketActions.VisibilityAct.ShowAll"));