From c916313e755857aa253a66dd584a74fd46cefcb8 Mon Sep 17 00:00:00 2001 From: Dan Royer Date: Fri, 22 Dec 2023 16:00:57 -0800 Subject: [PATCH] added webcampanel and icons --- .../marginallyclever/ro3/apps/RO3Frame.java | 30 +++++++++---- .../ro3/apps/actions/ExportScene.java | 5 ++- .../ro3/apps/actions/ImportScene.java | 5 ++- .../ro3/apps/actions/LoadScene.java | 2 + .../ro3/apps/actions/NewScene.java | 2 + .../ro3/apps/actions/RemoveNode.java | 2 +- .../ro3/apps/actions/SaveScene.java | 5 ++- .../ro3/apps/webcampanel/WebCamPanel.java | 40 ++++++++++++++++++ .../ro3/apps/about/about.html | 8 ++-- .../ro3/apps/actions/icons8-add-16-purple.png | Bin 0 -> 465 bytes .../ro3/apps/actions/icons8-export-16.png | Bin 0 -> 235 bytes .../ro3/apps/actions/icons8-import-16.png | Bin 0 -> 237 bytes .../ro3/apps/actions/icons8-load-16.png | Bin 0 -> 249 bytes .../ro3/apps/actions/icons8-new-16.png | Bin 0 -> 333 bytes .../ro3/apps/actions/icons8-save-16.png | Bin 0 -> 5104 bytes .../ro3/apps/actions/icons8-settings-16.png | Bin 0 -> 275 bytes .../ro3/apps/icons8-stop-16.png | Bin 0 -> 508 bytes 17 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/marginallyclever/ro3/apps/webcampanel/WebCamPanel.java create mode 100644 src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-add-16-purple.png create mode 100644 src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-export-16.png create mode 100644 src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-import-16.png create mode 100644 src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-load-16.png create mode 100644 src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-new-16.png create mode 100644 src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-save-16.png create mode 100644 src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-settings-16.png create mode 100644 src/main/resources/com/marginallyclever/ro3/apps/icons8-stop-16.png diff --git a/src/main/java/com/marginallyclever/ro3/apps/RO3Frame.java b/src/main/java/com/marginallyclever/ro3/apps/RO3Frame.java index c112aea95..9561364a9 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/RO3Frame.java +++ b/src/main/java/com/marginallyclever/ro3/apps/RO3Frame.java @@ -15,6 +15,7 @@ import com.marginallyclever.ro3.apps.logpanel.LogPanel; import com.marginallyclever.ro3.apps.nodedetailview.NodeDetailView; import com.marginallyclever.ro3.apps.nodetreeview.NodeTreeView; +import com.marginallyclever.ro3.apps.webcampanel.WebCamPanel; import com.marginallyclever.ro3.render.OpenGLPanel; import com.marginallyclever.ro3.render.Viewport; import com.marginallyclever.robotoverlord.RobotOverlord; @@ -31,24 +32,26 @@ import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.util.Objects; import java.util.prefs.Preferences; public class RO3Frame extends JFrame { private static final Logger logger = LoggerFactory.getLogger(RO3Frame.class); + private final List windows = new ArrayList<>(); + private final JFileChooser fileChooser; private final OpenGLPanel renderPanel; private final LogPanel logPanel; private final EditorPanel editPanel; - private final List windows = new ArrayList<>(); - private final JFileChooser fileChooser; + private final WebCamPanel webCamPanel; public RO3Frame() { super("Robot Overlord 3"); - setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); setLookAndFeel(); logPanel = new LogPanel(); editPanel = new EditorPanel(); renderPanel = new Viewport(); fileChooser = new JFileChooser(); + webCamPanel = new WebCamPanel(); initDocking(); createLayout(); @@ -82,14 +85,15 @@ private void initDocking() { } private void addQuitHandler() { + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { // when someone tries to close the app, confirm it. @Override public void windowClosing(WindowEvent e) { - if(confirmClose()) { - setDefaultCloseOperation(EXIT_ON_CLOSE); - } - super.windowClosing(e); + if(confirmClose()) { + setDefaultCloseOperation(EXIT_ON_CLOSE); + } + super.windowClosing(e); } }); @@ -148,13 +152,17 @@ private JMenu buildFileMenu() { menuFile.add(new JMenuItem(new LoadScene(loadRecentMenu,null,fileChooser))); menuFile.add(loadRecentMenu); menuFile.add(new JMenuItem(new ImportScene(fileChooser))); - - // TODO save vs save-as menuFile.add(new JMenuItem(new SaveScene(loadRecentMenu,fileChooser))); menuFile.add(new JMenuItem(new ExportScene())); menuFile.add(new JSeparator()); menuFile.add(new JMenuItem(new AbstractAction("Quit") { + { + putValue(Action.NAME, "Quit"); + putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Q, InputEvent.CTRL_DOWN_MASK)); + putValue(Action.SMALL_ICON, new ImageIcon(Objects.requireNonNull(getClass().getResource("icons8-stop-16.png")))); + putValue(Action.SHORT_DESCRIPTION, "Quit the application."); + } @Override public void actionPerformed(java.awt.event.ActionEvent e) { if(confirmClose()) { @@ -221,6 +229,10 @@ private void createLayout() { aboutView.add(new AboutPanel(), BorderLayout.CENTER); windows.add(aboutView); + DockingPanel webcamView = new DockingPanel("1331fbb0-ceda-4c67-b343-6539d4f939a1","USB Camera"); + webcamView.add(webCamPanel, BorderLayout.CENTER); + windows.add(webcamView); + // now that the main frame is set up with the defaults, we can restore the layout AppState.setPersistFile(new File("ro3.layout")); diff --git a/src/main/java/com/marginallyclever/ro3/apps/actions/ExportScene.java b/src/main/java/com/marginallyclever/ro3/apps/actions/ExportScene.java index 2a00af812..7b81b9015 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/actions/ExportScene.java +++ b/src/main/java/com/marginallyclever/ro3/apps/actions/ExportScene.java @@ -8,6 +8,7 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.io.File; +import java.util.Objects; /** *

Export the scene and all the assets used to a single file for sharing on another computer. @@ -18,7 +19,9 @@ public class ExportScene extends AbstractAction { private static final JFileChooser chooser = new JFileChooser(); public ExportScene() { - super("Export Scene"); + super(); + putValue(Action.NAME,"Export Scene"); + putValue(Action.SMALL_ICON,new ImageIcon(Objects.requireNonNull(getClass().getResource("icons8-export-16.png")))); putValue(SHORT_DESCRIPTION,"Export the scene and all the assets used to a single file."); } diff --git a/src/main/java/com/marginallyclever/ro3/apps/actions/ImportScene.java b/src/main/java/com/marginallyclever/ro3/apps/actions/ImportScene.java index 19740f500..c52c27e6b 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/actions/ImportScene.java +++ b/src/main/java/com/marginallyclever/ro3/apps/actions/ImportScene.java @@ -15,6 +15,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.security.InvalidParameterException; +import java.util.Objects; /** * Load a Scene into the existing Scene. @@ -24,8 +25,10 @@ public class ImportScene extends AbstractAction { private final JFileChooser chooser; public ImportScene(JFileChooser chooser) { - super("Import Scene"); + super(); this.chooser = chooser; + putValue(Action.NAME,"Import Scene"); + putValue(Action.SMALL_ICON,new ImageIcon(Objects.requireNonNull(getClass().getResource("icons8-import-16.png")))); putValue(SHORT_DESCRIPTION,"Load a Scene into the existing Scene."); } diff --git a/src/main/java/com/marginallyclever/ro3/apps/actions/LoadScene.java b/src/main/java/com/marginallyclever/ro3/apps/actions/LoadScene.java index 9d85c08a4..4c769576d 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/actions/LoadScene.java +++ b/src/main/java/com/marginallyclever/ro3/apps/actions/LoadScene.java @@ -15,6 +15,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.security.InvalidParameterException; +import java.util.Objects; /** * Load a scene from a file. Completely replaces the current Scene. @@ -48,6 +49,7 @@ public LoadScene(RecentFilesMenu menu, String filePath,JFileChooser chooser) { this.menu = menu; this.filePath = filePath; putValue(Action.NAME,filePath==null || filePath.isEmpty() ? "Load Scene" : filePath); + putValue(Action.SMALL_ICON,new ImageIcon(Objects.requireNonNull(getClass().getResource("icons8-load-16.png")))); putValue(SHORT_DESCRIPTION,"Load a scene from a file. Completely replaces the current Scene."); } diff --git a/src/main/java/com/marginallyclever/ro3/apps/actions/NewScene.java b/src/main/java/com/marginallyclever/ro3/apps/actions/NewScene.java index e53adc80e..608424853 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/actions/NewScene.java +++ b/src/main/java/com/marginallyclever/ro3/apps/actions/NewScene.java @@ -9,6 +9,7 @@ import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Reset the scene to a new empty scene. @@ -19,6 +20,7 @@ public class NewScene extends AbstractAction { public NewScene() { super(); putValue(Action.NAME,"New"); + putValue(Action.SMALL_ICON,new ImageIcon(Objects.requireNonNull(getClass().getResource("icons8-new-16.png")))); putValue(SHORT_DESCRIPTION,"Reset the scene to a new empty scene."); } diff --git a/src/main/java/com/marginallyclever/ro3/apps/actions/RemoveNode.java b/src/main/java/com/marginallyclever/ro3/apps/actions/RemoveNode.java index 9d7d19f62..e0e28d60e 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/actions/RemoveNode.java +++ b/src/main/java/com/marginallyclever/ro3/apps/actions/RemoveNode.java @@ -10,10 +10,10 @@ public class RemoveNode extends AbstractAction { private final NodeTreeView nodeTreeView; public RemoveNode(NodeTreeView nodeTreeView) { super(); + this.nodeTreeView = nodeTreeView; putValue(Action.NAME,"Remove"); putValue(Action.SMALL_ICON,new ImageIcon(Objects.requireNonNull(getClass().getResource("icons8-delete-16.png")))); putValue(SHORT_DESCRIPTION,"Remove the selected node(s)."); - this.nodeTreeView = nodeTreeView; } @Override diff --git a/src/main/java/com/marginallyclever/ro3/apps/actions/SaveScene.java b/src/main/java/com/marginallyclever/ro3/apps/actions/SaveScene.java index a59228134..f3ef2d409 100644 --- a/src/main/java/com/marginallyclever/ro3/apps/actions/SaveScene.java +++ b/src/main/java/com/marginallyclever/ro3/apps/actions/SaveScene.java @@ -12,6 +12,7 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.Objects; /** * Save the entire scene to a file. @@ -22,9 +23,11 @@ public class SaveScene extends AbstractAction { private final RecentFilesMenu menu; public SaveScene(RecentFilesMenu menu,JFileChooser chooser) { - super("Save Scene"); + super(); this.chooser = chooser; this.menu = menu; + putValue(Action.NAME,"Save Scene"); + putValue(Action.SMALL_ICON,new ImageIcon(Objects.requireNonNull(getClass().getResource("icons8-save-16.png")))); putValue(SHORT_DESCRIPTION,"Save to a file."); } diff --git a/src/main/java/com/marginallyclever/ro3/apps/webcampanel/WebCamPanel.java b/src/main/java/com/marginallyclever/ro3/apps/webcampanel/WebCamPanel.java new file mode 100644 index 000000000..20292e265 --- /dev/null +++ b/src/main/java/com/marginallyclever/ro3/apps/webcampanel/WebCamPanel.java @@ -0,0 +1,40 @@ +package com.marginallyclever.ro3.apps.webcampanel; + +import com.github.sarxos.webcam.Webcam; +import com.github.sarxos.webcam.WebcamPanel; +import com.github.sarxos.webcam.WebcamResolution; + +import javax.swing.*; +import java.awt.*; + +public class WebCamPanel extends JPanel { + private final Webcam webcam; + private final WebcamPanel panel; + + public WebCamPanel() { + super(new BorderLayout()); + setName("webcam"); + + webcam = Webcam.getDefault(); + Dimension size = WebcamResolution.QVGA.getSize(); + webcam.setViewSize(size); + + panel = new WebcamPanel(webcam, size, false); + panel.setFPSDisplayed(true); + add(panel, BorderLayout.CENTER); + panel.start(); + panel.pause(); + } + + @Override + public void addNotify() { + super.addNotify(); + panel.resume(); + } + + @Override + public void removeNotify() { + super.removeNotify(); + panel.pause(); + } +} diff --git a/src/main/resources/com/marginallyclever/ro3/apps/about/about.html b/src/main/resources/com/marginallyclever/ro3/apps/about/about.html index 5f1ef3897..70b223f36 100644 --- a/src/main/resources/com/marginallyclever/ro3/apps/about/about.html +++ b/src/main/resources/com/marginallyclever/ro3/apps/about/about.html @@ -1,8 +1,10 @@ - + +

Hello, World!

Welcome to Robot Overlord 3.

-

Read the friendly manual

+

Read the friendly manual

Discord: join us!

Github.com: read the source

Created by Marginally Clever Robots

- \ No newline at end of file + + \ No newline at end of file diff --git a/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-add-16-purple.png b/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-add-16-purple.png new file mode 100644 index 0000000000000000000000000000000000000000..29c0636c20d94c2ae7555d72ff61fc360cd79b9b GIT binary patch literal 465 zcmV;?0WSWDP)`@CW4*n2pe#*9?(GW$SmneD+sCe|N zakNn$##&hztEFK!uopvD7EwoZ8vm~E-bZlnCAjDpuu?2x$t1CXZTh?l8J7bh9w_jU zuYi}}&O>nCCt$%SVy+;>2G+kl&t4+wmnfpsQGN)sc_G+x1x)7%Y`|V3Vc8^MsYt|P zp@?~-h+DU!Z-%050aFGZlUY0_GI=(zNKrv3eu*M~FArZ27f0ji97fYPjHJG@0b2~w zOV-az863vGp`qkg3?{w6lK8>~tOh>Fpp>8JYtQVj{Ab?>;)A1MFmIOu00000NkvXX Hu0mjf09V1o literal 0 HcmV?d00001 diff --git a/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-export-16.png b/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-export-16.png new file mode 100644 index 0000000000000000000000000000000000000000..38b7638c7bb07b8d1dea317a8b98cdd2951e0f03 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;joN zi(^Q|oTHZw`4|I5S|9eWa9w;UGepa(dO_gD$d(n6?V3ltjxfD(T)3Szopr0MCe6wEzGB literal 0 HcmV?d00001 diff --git a/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-import-16.png b/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-import-16.png new file mode 100644 index 0000000000000000000000000000000000000000..ee619c8c6c4232e68cfc2696b3ffa00dda6b97f2 GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;kDd zi(^Q|oUNC&`IrKES|9R@D6_RXDIIvQfkTC}PxVZm!$v-qmg7kiau^!b9n^MCvOZrh z>G3W5mzmt!dp>G@WbTwdRd9UGf!7R@M;OeH&f3Dtv(fz)%LU!WZpKf?bjy4hIQtUc zWpfmD1)fmUWhyCHx8d5IZixpW3x9T2=CsK1E&L$lCefbz<#o%BOsOQ@)c`qnGF9hP3%Ed_KcgQu&X%Q~loCIAvWR_g!& literal 0 HcmV?d00001 diff --git a/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-load-16.png b/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-load-16.png new file mode 100644 index 0000000000000000000000000000000000000000..e044f9a7cb34002df4e7559065b705077acfbbbf GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;j-U zi(^Q|oaBTBVlxUprN3wiw*Q}e*!cYaU-m!GocG(r8&UiD`~T$g|NhLc`IRnylg;MO zFaG_C!XG3yd^n}QAx(fy@>>0;-PWFLJ`Ts#>)ZN7n7f|IGbC@~H7Nhip8QMt!iJxG zH(nYrJLF7|xX4x_SMmA2sg-bELvhNj|L6X$|0r$n@kqGj2E~9FqyGilriQ<$_J04M vzjgEXr;4W=uOvw{@XpxHD$#6U=gP3`{YTzA$A7Q`oz39s>gTe~DWM4fb&+PZ literal 0 HcmV?d00001 diff --git a/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-new-16.png b/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-new-16.png new file mode 100644 index 0000000000000000000000000000000000000000..b85ce6afc97f42057ac4308f48612c3f038a3a77 GIT binary patch literal 333 zcmV-T0kZyyP)Z7vou~hQI$-ht=K=ly-wzo6`h7t6HcxDu_x1(<|9wCgj(_df{r7vn?hK+_P_pL}*mh(CAcp8HB*Fzh|JUre_5bGq z0}KOxAJ9Eb*aatl{;xmui5ffje?o=t_d#v9Uk7xS{W_?d f{d>Q*14se@_naqCRBAGN00000NkvXXu0mjfTo0X- literal 0 HcmV?d00001 diff --git a/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-save-16.png b/src/main/resources/com/marginallyclever/ro3/apps/actions/icons8-save-16.png new file mode 100644 index 0000000000000000000000000000000000000000..bfdc9b51a1d6593c9fbb3cf92e9a3e0b220f314e GIT binary patch literal 5104 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 VmJRnM?f^Z+;OXk;vd$@?2><{&Xnp_y literal 0 HcmV?d00001 diff --git a/src/main/resources/com/marginallyclever/ro3/apps/icons8-stop-16.png b/src/main/resources/com/marginallyclever/ro3/apps/icons8-stop-16.png new file mode 100644 index 0000000000000000000000000000000000000000..201d4cc26f71ad6f1d35d747027b8203ad3829e0 GIT binary patch literal 508 zcmVK}mY>4SXt9-`|lWF`x-u|?F;QCp7p_PO3K z+zH9u1?qIedi4e}wE(e_C33m!619>x_3`l~?7aha;;j8O&RSlg_@WImy$YK2nRdb2 zI#YA=RUv=o0)PA*miH4V6k#`RkYO^BtO|cMKE>(7N3cdeg8bnd1*t_Qqp`nWYZ*MP zYr2Rv;syCab^*=IF$zVh!s~+J51=nWyTA%ZOqp4$3a=uQKVTW-_9OD1W-O2eO8 y$V&KXiuLu*(thG|fQd=}b!fzEmd+~Tf0;ilnW3g~_o@c~0000