diff --git a/jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java b/jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java index e36319c0bc..0d6ee82c6d 100644 --- a/jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java +++ b/jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java @@ -17,6 +17,8 @@ import com.jme3.system.*; import com.jme3.system.JmeContext.Type; import com.jme3.util.AndroidScreenshots; +import com.jme3.util.functional.VoidFunction; + import java.io.File; import java.io.IOException; import java.io.OutputStream; @@ -35,6 +37,18 @@ public class JmeAndroidSystem extends JmeSystemDelegate { } catch (UnsatisfiedLinkError e) { } } + + public JmeAndroidSystem(){ + setErrorMessageHandler((message) -> { + String finalMsg = message; + String finalTitle = "Error in application"; + Context context = JmeAndroidSystem.getView().getContext(); + view.getHandler().post(() -> { + AlertDialog dialog = new AlertDialog.Builder(context).setTitle(finalTitle).setMessage(finalMsg).create(); + dialog.show(); + }); + }); + } @Override public URL getPlatformAssetConfigURL() { @@ -57,26 +71,8 @@ public void writeImageFile(OutputStream outStream, String format, ByteBuffer ima bitmapImage.recycle(); } - @Override - public void showErrorDialog(String message) { - final String finalMsg = message; - final String finalTitle = "Error in application"; - final Context context = JmeAndroidSystem.getView().getContext(); - view.getHandler().post(new Runnable() { - @Override - public void run() { - AlertDialog dialog = new AlertDialog.Builder(context) - .setTitle(finalTitle).setMessage(finalMsg).create(); - dialog.show(); - } - }); - } - @Override - public boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry) { - return true; - } @Override public JmeContext newContext(AppSettings settings, Type contextType) { diff --git a/jme3-awt-dialogs/build.gradle b/jme3-awt-dialogs/build.gradle new file mode 100644 index 0000000000..9dd715218e --- /dev/null +++ b/jme3-awt-dialogs/build.gradle @@ -0,0 +1,3 @@ +dependencies { + api project(':jme3-core') +} diff --git a/jme3-awt-dialogs/src/main/java/com/jme3/awt/AWTErrorDialog.java b/jme3-awt-dialogs/src/main/java/com/jme3/awt/AWTErrorDialog.java new file mode 100644 index 0000000000..002f80a20b --- /dev/null +++ b/jme3-awt-dialogs/src/main/java/com/jme3/awt/AWTErrorDialog.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2009-2022 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.awt; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; + +/** + * Simple dialog for displaying error messages, + * + * @author kwando + */ +public class AWTErrorDialog extends JDialog { + public static String DEFAULT_TITLE = "Error in application"; + public static int PADDING = 8; + + /** + * Create a new Dialog with a title and a message. + * + * @param message the message to display + * @param title the title to display + */ + protected AWTErrorDialog(String message, String title) { + setTitle(title); + setSize(new Dimension(600, 400)); + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + setLocationRelativeTo(null); + + Container container = getContentPane(); + container.setLayout(new BorderLayout()); + + JTextArea textArea = new JTextArea(); + textArea.setText(message); + textArea.setEditable(false); + textArea.setMargin(new Insets(PADDING, PADDING, PADDING, PADDING)); + add(new JScrollPane(textArea), BorderLayout.CENTER); + + final JDialog dialog = this; + JButton button = new JButton(new AbstractAction("OK"){ + @Override + public void actionPerformed(ActionEvent e) { + dialog.dispose(); + } + }); + add(button, BorderLayout.SOUTH); + } + + protected AWTErrorDialog(String message){ + this(message, DEFAULT_TITLE); + } + + /** + * Show a dialog with the provided message. + * + * @param message the message to display + */ + public static void showDialog(String message) { + AWTErrorDialog dialog = new AWTErrorDialog(message); + dialog.setVisible(true); + } +} diff --git a/jme3-desktop/src/main/java/com/jme3/app/SettingsDialog.java b/jme3-awt-dialogs/src/main/java/com/jme3/awt/AWTSettingsDialog.java similarity index 82% rename from jme3-desktop/src/main/java/com/jme3/app/SettingsDialog.java rename to jme3-awt-dialogs/src/main/java/com/jme3/awt/AWTSettingsDialog.java index 9e20251dac..34744b2298 100644 --- a/jme3-desktop/src/main/java/com/jme3/app/SettingsDialog.java +++ b/jme3-awt-dialogs/src/main/java/com/jme3/awt/AWTSettingsDialog.java @@ -29,9 +29,12 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.jme3.app; +package com.jme3.awt; +import com.jme3.asset.AssetNotFoundException; import com.jme3.system.AppSettings; +import com.jme3.system.JmeSystem; + import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; @@ -46,6 +49,8 @@ import java.util.List; import java.util.ResourceBundle; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.BackingStoreException; @@ -64,24 +69,23 @@ * @author Eric Woroshow * @author Joshua Slack - reworked for proper use of GL commands. */ -public final class SettingsDialog extends JFrame { +public final class AWTSettingsDialog extends JFrame { public static interface SelectionListener { public void onSelection(int selection); } - private static final Logger logger = Logger.getLogger(SettingsDialog.class.getName()); + + private static final Logger logger = Logger.getLogger(AWTSettingsDialog.class.getName()); private static final long serialVersionUID = 1L; - public static final int NO_SELECTION = 0, - APPROVE_SELECTION = 1, - CANCEL_SELECTION = 2; - + public static final int NO_SELECTION = 0, APPROVE_SELECTION = 1, CANCEL_SELECTION = 2; + // Resource bundle for i18n. ResourceBundle resourceBundle = ResourceBundle.getBundle("com.jme3.app/SettingsDialog"); - + // the instance being configured private final AppSettings source; - + // Title Image private URL imageFile = null; // Array of supported display modes @@ -109,7 +113,70 @@ public static interface SelectionListener { private int minWidth = 0; private int minHeight = 0; - + + public static boolean showDialog(AppSettings sourceSettings) { + return showDialog(sourceSettings, true); + } + + public static boolean showDialog(AppSettings sourceSettings, boolean loadSettings) { + String iconPath = sourceSettings.getSettingsDialogImage(); + final URL iconUrl = JmeSystem.class.getResource(iconPath.startsWith("/") ? iconPath : "/" + iconPath); + if (iconUrl == null) { + throw new AssetNotFoundException(sourceSettings.getSettingsDialogImage()); + } + return showDialog(sourceSettings, iconUrl, loadSettings); + } + + public static boolean showDialog(AppSettings sourceSettings, String imageFile, boolean loadSettings) { + return showDialog(sourceSettings, getURL(imageFile), loadSettings); + } + + public static boolean showDialog(AppSettings sourceSettings, URL imageFile, boolean loadSettings) { + if (SwingUtilities.isEventDispatchThread()) { + throw new IllegalStateException("Cannot run from EDT"); + } + if (GraphicsEnvironment.isHeadless()) { + throw new IllegalStateException("Cannot show dialog in headless environment"); + } + + AppSettings settings = new AppSettings(false); + settings.copyFrom(sourceSettings); + + Object lock = new Object(); + AtomicBoolean done = new AtomicBoolean(); + AtomicInteger result = new AtomicInteger(); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + final SelectionListener selectionListener = new SelectionListener() { + @Override + public void onSelection(int selection) { + synchronized (lock) { + done.set(true); + result.set(selection); + lock.notifyAll(); + } + } + }; + AWTSettingsDialog dialog = new AWTSettingsDialog(settings, imageFile, loadSettings); + dialog.setSelectionListener(selectionListener); + dialog.showDialog(); + } + }); + synchronized (lock) { + while (!done.get()) { + try { + lock.wait(); + } catch (InterruptedException ex) { + } + } + } + + sourceSettings.copyFrom(settings); + + return result.get() == AWTSettingsDialog.APPROVE_SELECTION; + } + /** * Instantiate a SettingsDialog for the primary display. * @@ -123,12 +190,12 @@ public static interface SelectionListener { * @throws IllegalArgumentException * if the source is null */ - public SettingsDialog(AppSettings source, String imageFile, boolean loadSettings) { + protected AWTSettingsDialog(AppSettings source, String imageFile, boolean loadSettings) { this(source, getURL(imageFile), loadSettings); } /** - * Instantiate a SettingsDialog for the primary display. + * /** Instantiate a SettingsDialog for the primary display. * * @param source * the AppSettings object (not null) @@ -140,7 +207,7 @@ public SettingsDialog(AppSettings source, String imageFile, boolean loadSettings * @throws IllegalArgumentException * if the source is null */ - public SettingsDialog(AppSettings source, URL imageFile, boolean loadSettings) { + protected AWTSettingsDialog(AppSettings source, URL imageFile, boolean loadSettings) { if (source == null) { throw new IllegalArgumentException("Settings source cannot be null"); } @@ -148,32 +215,31 @@ public SettingsDialog(AppSettings source, URL imageFile, boolean loadSettings) { this.source = source; this.imageFile = imageFile; - //setModal(true); + // setModal(true); setAlwaysOnTop(true); setResizable(false); AppSettings registrySettings = new AppSettings(true); String appTitle; - if(source.getTitle()!=null){ + if (source.getTitle() != null) { appTitle = source.getTitle(); - }else{ - appTitle = registrySettings.getTitle(); + } else { + appTitle = registrySettings.getTitle(); } - + minWidth = source.getMinWidth(); minHeight = source.getMinHeight(); - + try { registrySettings.load(appTitle); } catch (BackingStoreException ex) { - logger.log(Level.WARNING, - "Failed to load settings", ex); + logger.log(Level.WARNING, "Failed to load settings", ex); } if (loadSettings) { source.copyFrom(registrySettings); - } else if(!registrySettings.isEmpty()) { + } else if (!registrySettings.isEmpty()) { source.mergeFrom(registrySettings); } @@ -183,17 +249,13 @@ public SettingsDialog(AppSettings source, URL imageFile, boolean loadSettings) { Arrays.sort(modes, new DisplayModeSorter()); DisplayMode[] merged = new DisplayMode[modes.length + windowDefaults.length]; - + int wdIndex = 0; int dmIndex = 0; int mergedIndex; - - for (mergedIndex = 0; - mergedIndex= modes.length) { merged[mergedIndex] = windowDefaults[wdIndex++]; } else if (wdIndex >= windowDefaults.length) { @@ -213,13 +275,13 @@ public SettingsDialog(AppSettings source, URL imageFile, boolean loadSettings) { merged[mergedIndex] = windowDefaults[wdIndex++]; } } - + if (merged.length == mergedIndex) { windowModes = merged; } else { windowModes = Arrays.copyOfRange(merged, 0, mergedIndex); } - + createUI(); } @@ -252,9 +314,6 @@ public void setMinHeight(int minHeight) { this.minHeight = minHeight; } - - - /** * setImage sets the background image of the dialog. * @@ -266,7 +325,7 @@ public void setImage(String image) { URL file = new URL("file:" + image); setImage(file); } catch (MalformedURLException e) { - logger.log(Level.WARNING, "Couldn’t read from file '" + image + "'", e); + logger.log(Level.WARNING, "Couldn’t read from file '" + image + "'", e); } } @@ -283,21 +342,21 @@ public void setImage(URL image) { } /** - * showDialog sets this dialog as visible, and brings it to - * the front. + * showDialog sets this dialog as visible, and brings it to the + * front. */ public void showDialog() { setLocationRelativeTo(null); - setVisible(true); + setVisible(true); toFront(); } - + /** * init creates the components to use the dialog. */ private void createUI() { GridBagConstraints gbc; - + JPanel mainPanel = new JPanel(new GridBagLayout()); addWindowListener(new WindowAdapter() { @@ -310,13 +369,13 @@ public void windowClosing(WindowEvent e) { }); if (source.getIcons() != null) { - safeSetIconImages( Arrays.asList((BufferedImage[]) source.getIcons()) ); + safeSetIconImages(Arrays.asList((BufferedImage[]) source.getIcons())); } setTitle(MessageFormat.format(resourceBundle.getString("frame.title"), source.getTitle())); - + // The buttons... - JButton ok = new JButton(resourceBundle.getString("button.ok")); + JButton ok = new JButton(resourceBundle.getString("button.ok")); JButton cancel = new JButton(resourceBundle.getString("button.cancel")); icon = new JLabel(imageFile != null ? new ImageIcon(imageFile) : null); @@ -330,8 +389,7 @@ public void keyPressed(KeyEvent e) { setUserSelection(APPROVE_SELECTION); dispose(); } - } - else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { setUserSelection(CANCEL_SELECTION); dispose(); } @@ -357,10 +415,10 @@ public void actionPerformed(ActionEvent e) { }); vsyncBox = new JCheckBox(resourceBundle.getString("checkbox.vsync")); vsyncBox.setSelected(source.isVSync()); - + gammaBox = new JCheckBox(resourceBundle.getString("checkbox.gamma")); gammaBox.setSelected(source.isGammaCorrection()); - + gbc = new GridBagConstraints(); gbc.weightx = 0.5; gbc.gridx = 0; @@ -370,20 +428,19 @@ public void actionPerformed(ActionEvent e) { mainPanel.add(fullscreenBox, gbc); gbc = new GridBagConstraints(); gbc.weightx = 0.5; - // gbc.insets = new Insets(4, 16, 0, 4); + // gbc.insets = new Insets(4, 16, 0, 4); gbc.gridx = 2; - // gbc.gridwidth = 2; + // gbc.gridwidth = 2; gbc.gridy = 1; gbc.anchor = GridBagConstraints.EAST; mainPanel.add(vsyncBox, gbc); gbc = new GridBagConstraints(); gbc.weightx = 0.5; gbc.gridx = 3; - gbc.gridy = 1; + gbc.gridy = 1; gbc.anchor = GridBagConstraints.WEST; mainPanel.add(gammaBox, gbc); - gbc = new GridBagConstraints(); gbc.insets = new Insets(4, 4, 4, 4); gbc.gridx = 0; @@ -432,7 +489,7 @@ public void actionPerformed(ActionEvent e) { gbc.gridy = 3; gbc.anchor = GridBagConstraints.WEST; mainPanel.add(antialiasCombo, gbc); - + // Set the button action listeners. Cancel disposes without saving, OK // saves. ok.addActionListener(new ActionListener() { @@ -442,10 +499,14 @@ public void actionPerformed(ActionEvent e) { if (verifyAndSaveCurrentSelection()) { setUserSelection(APPROVE_SELECTION); dispose(); - - // System.gc() should be called to prevent "X Error of failed request: RenderBadPicture (invalid Picture parameter)" - // on Linux when using AWT/Swing + GLFW. - // For more info see: https://github.com/LWJGL/lwjgl3/issues/149, https://hub.jmonkeyengine.org/t/experimenting-lwjgl3/37275 + + // System.gc() should be called to prevent "X Error of + // failed request: RenderBadPicture (invalid Picture + // parameter)" + // on Linux when using AWT/Swing + GLFW. + // For more info see: + // https://github.com/LWJGL/lwjgl3/issues/149, + // https://hub.jmonkeyengine.org/t/experimenting-lwjgl3/37275 System.gc(); System.gc(); } @@ -466,7 +527,7 @@ public void actionPerformed(ActionEvent e) { gbc.gridwidth = 2; gbc.gridy = 4; gbc.anchor = GridBagConstraints.EAST; - mainPanel.add(ok, gbc); + mainPanel.add(ok, gbc); gbc = new GridBagConstraints(); gbc.insets = new Insets(4, 16, 4, 4); gbc.gridx = 2; @@ -484,35 +545,42 @@ public void actionPerformed(ActionEvent e) { this.getContentPane().add(mainPanel); pack(); - + mainPanel.getRootPane().setDefaultButton(ok); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - // Fill in the combos once the window has opened so that the insets can be read. - // The assumption is made that the settings window and the display window will have the - // same insets as that is used to resize the "full screen windowed" mode appropriately. + // Fill in the combos once the window has opened so that the + // insets can be read. + // The assumption is made that the settings window and the + // display window will have the + // same insets as that is used to resize the "full screen + // windowed" mode appropriately. updateResolutionChoices(); if (source.getWidth() != 0 && source.getHeight() != 0) { - displayResCombo.setSelectedItem(source.getWidth() + " x " - + source.getHeight()); + displayResCombo.setSelectedItem(source.getWidth() + " x " + source.getHeight()); } else { - displayResCombo.setSelectedIndex(displayResCombo.getItemCount()-1); + displayResCombo.setSelectedIndex(displayResCombo.getItemCount() - 1); } updateAntialiasChoices(); colorDepthCombo.setSelectedItem(source.getBitsPerPixel() + " bpp"); } - }); - + }); + } - /* Access JDialog.setIconImages by reflection in case we're running on JRE < 1.6 */ + /* + * Access JDialog.setIconImages by reflection in case we're running on JRE < + * 1.6 + */ private void safeSetIconImages(List icons) { try { - // Due to Java bug 6445278, we try to set icon on our shared owner frame first. - // Otherwise, our alt-tab icon will be the Java default under Windows. + // Due to Java bug 6445278, we try to set icon on our shared owner + // frame first. + // Otherwise, our alt-tab icon will be the Java default under + // Windows. Window owner = getOwner(); if (owner != null) { Method setIconImages = owner.getClass().getMethod("setIconImages", List.class); @@ -591,7 +659,7 @@ private boolean verifyAndSaveCurrentSelection() { } if (valid) { - //use the AppSettings class to save it to backing store + // use the AppSettings class to save it to backing store source.setWidth(width); source.setHeight(height); source.setBitsPerPixel(depth); @@ -599,7 +667,7 @@ private boolean verifyAndSaveCurrentSelection() { source.setFullscreen(fullscreen); source.setVSync(vsync); source.setGammaCorrection(gamma); - //source.setRenderer(renderer); + // source.setRenderer(renderer); source.setSamples(multisample); String appTitle = source.getTitle(); @@ -607,13 +675,10 @@ private boolean verifyAndSaveCurrentSelection() { try { source.save(appTitle); } catch (BackingStoreException ex) { - logger.log(Level.WARNING, - "Failed to save setting changes", ex); + logger.log(Level.WARNING, "Failed to save setting changes", ex); } } else { - showError( - this, - resourceBundle.getString("error.unsupportedmode")); + showError(this, resourceBundle.getString("error.unsupportedmode")); } return valid; @@ -623,7 +688,7 @@ private boolean verifyAndSaveCurrentSelection() { * setUpChooser retrieves all available display modes and * places them in a JComboBox. The resolution specified by * AppSettings is used as the default value. - * + * * @return the combo box of display modes. */ private JComboBox setUpResolutionChooser() { @@ -668,7 +733,7 @@ private void updateDisplayChoices() { displayFreqCombo.setModel(new DefaultComboBoxModel<>(freqs)); // Try to reset freq displayFreqCombo.setSelectedItem(displayFreq); - + if (!displayFreqCombo.getSelectedItem().equals(displayFreq)) { // Cannot find saved frequency in available frequencies. // Choose the closest one to 60 Hz. @@ -684,21 +749,17 @@ private void updateDisplayChoices() { */ private void updateResolutionChoices() { if (!fullscreenBox.isSelected()) { - displayResCombo.setModel(new DefaultComboBoxModel<>( - getWindowedResolutions(windowModes))); + displayResCombo.setModel(new DefaultComboBoxModel<>(getWindowedResolutions(windowModes))); if (displayResCombo.getItemCount() > 0) { - displayResCombo.setSelectedIndex(displayResCombo.getItemCount()-1); + displayResCombo.setSelectedIndex(displayResCombo.getItemCount() - 1); } - colorDepthCombo.setModel(new DefaultComboBoxModel<>(new String[]{ - "24 bpp", "16 bpp"})); - displayFreqCombo.setModel(new DefaultComboBoxModel<>( - new String[]{resourceBundle.getString("refresh.na")})); + colorDepthCombo.setModel(new DefaultComboBoxModel<>(new String[] { "24 bpp", "16 bpp" })); + displayFreqCombo.setModel(new DefaultComboBoxModel<>(new String[] { resourceBundle.getString("refresh.na") })); displayFreqCombo.setEnabled(false); } else { - displayResCombo.setModel(new DefaultComboBoxModel<>( - getResolutions(modes, Integer.MAX_VALUE, Integer.MAX_VALUE))); + displayResCombo.setModel(new DefaultComboBoxModel<>(getResolutions(modes, Integer.MAX_VALUE, Integer.MAX_VALUE))); if (displayResCombo.getItemCount() > 0) { - displayResCombo.setSelectedIndex(displayResCombo.getItemCount()-1); + displayResCombo.setSelectedIndex(displayResCombo.getItemCount() - 1); } displayFreqCombo.setEnabled(true); updateDisplayChoices(); @@ -708,9 +769,9 @@ private void updateResolutionChoices() { private void updateAntialiasChoices() { // maybe in the future will add support for determining this info // through PBuffer - String[] choices = new String[]{resourceBundle.getString("antialias.disabled"), "2x", "4x", "6x", "8x", "16x"}; + String[] choices = new String[] { resourceBundle.getString("antialias.disabled"), "2x", "4x", "6x", "8x", "16x" }; antialiasCombo.setModel(new DefaultComboBoxModel<>(choices)); - antialiasCombo.setSelectedItem(choices[Math.min(source.getSamples()/2,5)]); + antialiasCombo.setSelectedItem(choices[Math.min(source.getSamples() / 2, 5)]); } // @@ -732,19 +793,19 @@ private static URL getURL(String file) { } private static void showError(java.awt.Component parent, String message) { - JOptionPane.showMessageDialog(parent, message, "Error", - JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(parent, message, "Error", JOptionPane.ERROR_MESSAGE); } /** - * Returns every unique resolution from an array of DisplayModes - * where the resolution is greater than the configured minimums. + * Returns every unique resolution from an array of + * DisplayModes where the resolution is greater than the + * configured minimums. */ private String[] getResolutions(DisplayMode[] modes, int heightLimit, int widthLimit) { Insets insets = getInsets(); heightLimit -= insets.top + insets.bottom; widthLimit -= insets.left + insets.right; - + Set resolutions = new LinkedHashSet<>(modes.length); for (DisplayMode mode : modes) { int height = mode.getHeight(); @@ -756,7 +817,7 @@ private String[] getResolutions(DisplayMode[] modes, int heightLimit, int widthL if (width >= widthLimit) { width = widthLimit; } - + String res = width + " x " + height; resolutions.add(res); } @@ -764,16 +825,17 @@ private String[] getResolutions(DisplayMode[] modes, int heightLimit, int widthL return resolutions.toArray(new String[0]); } - + /** - * Returns every unique resolution from an array of DisplayModes - * where the resolution is greater than the configured minimums and the height - * is less than the current screen resolution. + * Returns every unique resolution from an array of + * DisplayModes where the resolution is greater than the + * configured minimums and the height is less than the current screen + * resolution. */ private String[] getWindowedResolutions(DisplayMode[] modes) { int maxHeight = 0; int maxWidth = 0; - + for (DisplayMode mode : modes) { if (maxHeight < mode.getHeight()) { maxHeight = mode.getHeight(); @@ -813,7 +875,8 @@ private static String[] getDepths(String resolution, DisplayMode[] modes) { } if (depths.isEmpty()) { - // add some default depths, possible system is multi-depth supporting + // add some default depths, possible system is multi-depth + // supporting depths.add("24 bpp"); } @@ -823,8 +886,7 @@ private static String[] getDepths(String resolution, DisplayMode[] modes) { /** * Returns every possible refresh rate for the given resolution. */ - private static String[] getFrequencies(String resolution, - DisplayMode[] modes) { + private static String[] getFrequencies(String resolution, DisplayMode[] modes) { List freqs = new ArrayList<>(4); for (DisplayMode mode : modes) { String res = mode.getWidth() + " x " + mode.getHeight(); @@ -841,13 +903,13 @@ private static String[] getFrequencies(String resolution, return freqs.toArray(new String[0]); } - + /** * Chooses the closest frequency to 60 Hz. * * @param resolution * @param modes - * @return + * @return */ private static String getBestFrequency(String resolution, DisplayMode[] modes) { int closest = Integer.MAX_VALUE; @@ -861,7 +923,7 @@ private static String getBestFrequency(String resolution, DisplayMode[] modes) { } } } - + if (closest != Integer.MAX_VALUE) { return closest + " Hz"; } else { @@ -870,8 +932,8 @@ private static String getBestFrequency(String resolution, DisplayMode[] modes) { } /** - * Utility class for sorting DisplayModes. Sorts by - * resolution, then bit depth, and then finally refresh rate. + * Utility class for sorting DisplayModes. Sorts by resolution, + * then bit depth, and then finally refresh rate. */ private class DisplayModeSorter implements Comparator { diff --git a/jme3-awt-dialogs/src/main/java/com/jme3/system/JmeDialogsFactoryImpl.java b/jme3-awt-dialogs/src/main/java/com/jme3/system/JmeDialogsFactoryImpl.java new file mode 100644 index 0000000000..7c5b12c97d --- /dev/null +++ b/jme3-awt-dialogs/src/main/java/com/jme3/system/JmeDialogsFactoryImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009-2022 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.system; + +import com.jme3.awt.AWTErrorDialog; +import com.jme3.awt.AWTSettingsDialog; + +public class JmeDialogsFactoryImpl implements JmeDialogsFactory { + + public boolean showSettingsDialog(AppSettings settings, boolean loadFromRegistry){ + return AWTSettingsDialog.showDialog(settings,loadFromRegistry); + } + + public void showErrorDialog(String message){ + AWTErrorDialog.showDialog(message); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/app/LegacyApplication.java b/jme3-core/src/main/java/com/jme3/app/LegacyApplication.java index 6849a35edd..626dec4bfd 100644 --- a/jme3-core/src/main/java/com/jme3/app/LegacyApplication.java +++ b/jme3-core/src/main/java/com/jme3/app/LegacyApplication.java @@ -667,10 +667,10 @@ public void handleError(String errMsg, Throwable t) { // Display error message on screen if not in headless mode if (context.getType() != JmeContext.Type.Headless) { if (t != null) { - JmeSystem.showErrorDialog(errMsg + "\n" + t.getClass().getSimpleName() + JmeSystem.handleErrorMessage(errMsg + "\n" + t.getClass().getSimpleName() + (t.getMessage() != null ? ": " + t.getMessage() : "")); } else { - JmeSystem.showErrorDialog(errMsg); + JmeSystem.handleErrorMessage(errMsg); } } diff --git a/jme3-core/src/main/java/com/jme3/system/JmeDialogsFactory.java b/jme3-core/src/main/java/com/jme3/system/JmeDialogsFactory.java new file mode 100644 index 0000000000..63113c729d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/system/JmeDialogsFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2009-2022 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.system; + +public interface JmeDialogsFactory { + /** + * Set a function to handler app settings. + * The default implementation shows a settings dialog if available. + * @param handler handler function that accepts as argument an instance of AppSettings + * to transform and a boolean with the value of true if the settings are expected to be loaded from + * the user registry. The handler function returns false if the configuration is interrupted (eg.the the dialog was closed) + * or true otherwise. + */ + public boolean showSettingsDialog(AppSettings settings, boolean loadFromRegistry); + + /** + * Set function to handle errors. + * The default implementation show a dialog if available. + * @param handler Consumer to which the error is passed as String + */ + public void showErrorDialog(String message); +} diff --git a/jme3-core/src/main/java/com/jme3/system/JmeSystem.java b/jme3-core/src/main/java/com/jme3/system/JmeSystem.java index 561926a9e7..4a832916c8 100644 --- a/jme3-core/src/main/java/com/jme3/system/JmeSystem.java +++ b/jme3-core/src/main/java/com/jme3/system/JmeSystem.java @@ -34,6 +34,7 @@ import com.jme3.asset.AssetManager; import com.jme3.audio.AudioRenderer; import com.jme3.input.SoftTextDialogInput; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -41,6 +42,8 @@ import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.nio.ByteBuffer; +import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; @@ -154,10 +157,7 @@ public static AssetManager newAssetManager() { return systemDelegate.newAssetManager(); } - public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) { - checkDelegate(); - return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry); - } + /** * Determine which Platform (operating system and architecture) the @@ -190,14 +190,44 @@ public static URL getPlatformAssetConfigURL() { * feels is appropriate. If this is a headless or an offscreen surface * context, this method should do nothing. * + * @deprecated Use JmeSystem.handleErrorMessage(String) instead * @param message The error message to display. May contain new line * characters. */ + @Deprecated public static void showErrorDialog(String message){ + handleErrorMessage(message); + } + + public static void handleErrorMessage(String message){ + checkDelegate(); + systemDelegate.handleErrorMessage(message); + } + + public static void setErrorMessageHandler(Consumer handler){ + checkDelegate(); + systemDelegate.setErrorMessageHandler(handler); + } + + + public static void handleSettings(AppSettings sourceSettings, boolean loadFromRegistry){ + checkDelegate(); + systemDelegate.handleSettings(sourceSettings, loadFromRegistry); + } + + public static void setSettingsHandler(BiFunction handler){ + checkDelegate(); + systemDelegate.setSettingsHandler(handler); + } + + + @Deprecated + public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) { checkDelegate(); - systemDelegate.showErrorDialog(message); + return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry); } + public static void initialize(AppSettings settings) { checkDelegate(); systemDelegate.initialize(settings); diff --git a/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java b/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java index 5e701f3a0f..35143fca4a 100644 --- a/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java +++ b/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java @@ -35,14 +35,18 @@ import com.jme3.asset.DesktopAssetManager; import com.jme3.audio.AudioRenderer; import com.jme3.input.SoftTextDialogInput; + import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.nio.ByteBuffer; import java.util.EnumMap; import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; @@ -58,6 +62,32 @@ public abstract class JmeSystemDelegate { protected Map storageFolders = new EnumMap<>(JmeSystem.StorageFolderType.class); protected SoftTextDialogInput softTextDialogInput = null; + protected Consumer errorMessageHandler = (message) -> { + JmeDialogsFactory dialogFactory = null; + try { + dialogFactory = (JmeDialogsFactory)Class.forName("com.jme3.system.JmeDialogsFactoryImpl").getConstructor().newInstance(); + } catch(ClassNotFoundException e){ + logger.warning("JmeDialogsFactory implementation not found."); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + e.printStackTrace(); + } + if(dialogFactory != null) dialogFactory.showErrorDialog(message); + else System.err.println(message); + }; + + protected BiFunction settingsHandler = (settings,loadFromRegistry) -> { + JmeDialogsFactory dialogFactory = null; + try { + dialogFactory = (JmeDialogsFactory)Class.forName("com.jme3.system.JmeDialogsFactoryImpl").getConstructor().newInstance(); + } catch(ClassNotFoundException e){ + logger.warning("JmeDialogsFactory implementation not found."); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + e.printStackTrace(); + } + if(dialogFactory != null) return dialogFactory.showSettingsDialog(settings, loadFromRegistry); + return true; + }; + public synchronized File getStorageFolder(JmeSystem.StorageFolderType type) { File storageFolder = null; @@ -133,9 +163,56 @@ public final AssetManager newAssetManager() { public abstract void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException; - public abstract void showErrorDialog(String message); + /** + * Set function to handle errors. + * The default implementation show a dialog if available. + * @param handler Consumer to which the error is passed as String + */ + public void setErrorMessageHandler(Consumer handler){ + errorMessageHandler = handler; + } + + /** + * Internal use only: submit an error to the error message handler + */ + public void handleErrorMessage(String message){ + if(errorMessageHandler != null) errorMessageHandler.accept(message); + } + + /** + * Set a function to handler app settings. + * The default implementation shows a settings dialog if available. + * @param handler handler function that accepts as argument an instance of AppSettings + * to transform and a boolean with the value of true if the settings are expected to be loaded from + * the user registry. The handler function returns false if the configuration is interrupted (eg.the the dialog was closed) + * or true otherwise. + */ + public void setSettingsHandler(BiFunction handler){ + settingsHandler = handler; + } + + /** + * Internal use only: summon the settings handler + */ + public boolean handleSettings(AppSettings settings, boolean loadFromRegistry){ + if(settingsHandler != null) return settingsHandler.apply(settings,loadFromRegistry); + return true; + } + + /** + * @deprecated Use JmeSystemDelegate.handleErrorMessage(String) instead + * @param message + */ + @Deprecated + public void showErrorDialog(String message){ + handleErrorMessage(message); + } + + @Deprecated + public boolean showSettingsDialog(AppSettings settings, boolean loadFromRegistry){ + return handleSettings(settings, loadFromRegistry); + } - public abstract boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry); private boolean is64Bit(String arch) { if (arch.equals("x86")) { diff --git a/jme3-core/src/test/java/com/jme3/system/MockJmeSystemDelegate.java b/jme3-core/src/test/java/com/jme3/system/MockJmeSystemDelegate.java index b9beac7f69..ca981877eb 100644 --- a/jme3-core/src/test/java/com/jme3/system/MockJmeSystemDelegate.java +++ b/jme3-core/src/test/java/com/jme3/system/MockJmeSystemDelegate.java @@ -43,15 +43,6 @@ public class MockJmeSystemDelegate extends JmeSystemDelegate { public void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException { } - @Override - public void showErrorDialog(String message) { - } - - @Override - public boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry) { - return false; - } - @Override public URL getPlatformAssetConfigURL() { return Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/General.cfg"); diff --git a/jme3-desktop/src/main/java/com/jme3/system/ErrorDialog.java b/jme3-desktop/src/main/java/com/jme3/system/ErrorDialog.java deleted file mode 100644 index 26f85be4e0..0000000000 --- a/jme3-desktop/src/main/java/com/jme3/system/ErrorDialog.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.jme3.system; - -import java.awt.BorderLayout; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import javax.swing.AbstractAction; -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; - -/** - * Simple dialog for displaying error messages, - * - * @author kwando - */ -public class ErrorDialog extends JDialog { - public static String DEFAULT_TITLE = "Error in application"; - public static int PADDING = 8; - - /** - * Create a new Dialog with a title and a message. - * - * @param message the message to display - * @param title the title to display - */ - public ErrorDialog(String message, String title) { - setTitle(title); - setSize(new Dimension(600, 400)); - setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - setLocationRelativeTo(null); - - Container container = getContentPane(); - container.setLayout(new BorderLayout()); - - JTextArea textArea = new JTextArea(); - textArea.setText(message); - textArea.setEditable(false); - textArea.setMargin(new Insets(PADDING, PADDING, PADDING, PADDING)); - add(new JScrollPane(textArea), BorderLayout.CENTER); - - final JDialog dialog = this; - JButton button = new JButton(new AbstractAction("OK"){ - @Override - public void actionPerformed(ActionEvent e) { - dialog.dispose(); - } - }); - add(button, BorderLayout.SOUTH); - } - - public ErrorDialog(String message){ - this(message, DEFAULT_TITLE); - } - - /** - * Show a dialog with the provided message. - * - * @param message the message to display - */ - public static void showDialog(String message) { - ErrorDialog dialog = new ErrorDialog(message); - dialog.setVisible(true); - } -} diff --git a/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java b/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java index 215a301e73..3a2882a241 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java +++ b/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java @@ -31,9 +31,6 @@ */ package com.jme3.system; -import com.jme3.app.SettingsDialog; -import com.jme3.app.SettingsDialog.SelectionListener; -import com.jme3.asset.AssetNotFoundException; import com.jme3.audio.AudioRenderer; import com.jme3.audio.openal.AL; import com.jme3.audio.openal.ALAudioRenderer; @@ -42,13 +39,16 @@ import com.jme3.system.JmeContext.Type; import com.jme3.texture.Image; import com.jme3.texture.image.ColorSpace; -import com.jme3.util.Screenshots; import jme3tools.converters.ImageToAwt; -import java.awt.EventQueue; -import java.awt.Graphics2D; -import java.awt.GraphicsEnvironment; -import java.awt.RenderingHints; +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.plugins.jpeg.JPEGImageWriteParam; +import javax.imageio.stream.ImageOutputStream; +import javax.imageio.stream.MemoryCacheImageOutputStream; +import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; @@ -57,17 +57,7 @@ import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.nio.ByteBuffer; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; -import javax.imageio.IIOImage; -import javax.imageio.ImageIO; -import javax.imageio.ImageWriteParam; -import javax.imageio.ImageWriter; -import javax.imageio.plugins.jpeg.JPEGImageWriteParam; -import javax.imageio.stream.ImageOutputStream; -import javax.imageio.stream.MemoryCacheImageOutputStream; -import javax.swing.SwingUtilities; /** * @@ -75,6 +65,9 @@ */ public class JmeDesktopSystem extends JmeSystemDelegate { + public JmeDesktopSystem() { + } + @Override public URL getPlatformAssetConfigURL() { return Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Desktop.cfg"); @@ -132,82 +125,6 @@ public void writeImageFile(OutputStream outStream, String format, ByteBuffer ima } } - @Override - public void showErrorDialog(String message) { - if (!GraphicsEnvironment.isHeadless()) { - final String msg = message; - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - ErrorDialog.showDialog(msg); - } - }); - } else { - System.err.println("[JME ERROR] " + message); - } - } - - @Override - public boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) { - if (SwingUtilities.isEventDispatchThread()) { - throw new IllegalStateException("Cannot run from EDT"); - } - if (GraphicsEnvironment.isHeadless()) { - throw new IllegalStateException("Cannot show dialog in headless environment"); - } - - final AppSettings settings = new AppSettings(false); - settings.copyFrom(sourceSettings); - String iconPath = sourceSettings.getSettingsDialogImage(); - if(iconPath == null){ - iconPath = ""; - } - final URL iconUrl = JmeSystem.class.getResource(iconPath.startsWith("/") ? iconPath : "/" + iconPath); - if (iconUrl == null) { - throw new AssetNotFoundException(sourceSettings.getSettingsDialogImage()); - } - - final AtomicBoolean done = new AtomicBoolean(); - final AtomicInteger result = new AtomicInteger(); - final Object lock = new Object(); - - final SelectionListener selectionListener = new SelectionListener() { - - @Override - public void onSelection(int selection) { - synchronized (lock) { - done.set(true); - result.set(selection); - lock.notifyAll(); - } - } - }; - SwingUtilities.invokeLater(new Runnable() { - - @Override - public void run() { - synchronized (lock) { - SettingsDialog dialog = new SettingsDialog(settings, iconUrl, loadFromRegistry); - dialog.setSelectionListener(selectionListener); - dialog.showDialog(); - } - } - }); - - synchronized (lock) { - while (!done.get()) { - try { - lock.wait(); - } catch (InterruptedException ex) { - } - } - } - - sourceSettings.copyFrom(settings); - - return result.get() == SettingsDialog.APPROVE_SELECTION; - } - @SuppressWarnings("unchecked") private JmeContext newContextLwjgl(AppSettings settings, JmeContext.Type type) { try { diff --git a/jme3-examples/build.gradle b/jme3-examples/build.gradle index a6e9888edf..fbed866b7d 100644 --- a/jme3-examples/build.gradle +++ b/jme3-examples/build.gradle @@ -25,6 +25,7 @@ dependencies { implementation project(':jme3-niftygui') implementation project(':jme3-plugins') implementation project(':jme3-terrain') + implementation project(':jme3-awt-dialogs') runtimeOnly project(':jme3-testdata') } diff --git a/jme3-ios/src/main/java/com/jme3/system/ios/JmeIosSystem.java b/jme3-ios/src/main/java/com/jme3/system/ios/JmeIosSystem.java index 820e387e54..77f41c1f82 100644 --- a/jme3-ios/src/main/java/com/jme3/system/ios/JmeIosSystem.java +++ b/jme3-ios/src/main/java/com/jme3/system/ios/JmeIosSystem.java @@ -35,6 +35,7 @@ import com.jme3.system.JmeContext; import com.jme3.system.JmeSystemDelegate; import com.jme3.system.NullContext; +import com.jme3.util.functional.VoidFunction; import com.jme3.audio.AudioRenderer; import com.jme3.audio.ios.IosAL; import com.jme3.audio.ios.IosALC; @@ -54,6 +55,13 @@ */ public class JmeIosSystem extends JmeSystemDelegate { + public JmeIosSystem() { + setErrorMessageHandler((message) -> { + showDialog(message); + System.err.println("JME APPLICATION ERROR:" + message); + }); + } + @Override public URL getPlatformAssetConfigURL() { return Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/IOS.cfg"); @@ -64,18 +72,11 @@ public void writeImageFile(OutputStream outStream, String format, ByteBuffer ima throw new UnsupportedOperationException("Not supported yet."); } - @Override - public void showErrorDialog(String message) { - showDialog(message); - System.err.println("JME APPLICATION ERROR:" + message); - } + private native void showDialog(String message); - @Override - public boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry) { - return true; - } + @Override public JmeContext newContext(AppSettings settings, JmeContext.Type contextType) { diff --git a/jme3-vr/src/main/java/com/jme3/app/VRApplication.java b/jme3-vr/src/main/java/com/jme3/app/VRApplication.java index 5d9be70c3f..02ab733230 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VRApplication.java +++ b/jme3-vr/src/main/java/com/jme3/app/VRApplication.java @@ -465,10 +465,10 @@ public void handleError(String errMsg, Throwable t){ // Display error message on screen if not in headless mode if (context.getType() != JmeContext.Type.Headless) { if (t != null) { - JmeSystem.showErrorDialog(errMsg + "\n" + t.getClass().getSimpleName() + + JmeSystem.handleErrorMessage(errMsg + "\n" + t.getClass().getSimpleName() + (t.getMessage() != null ? ": " + t.getMessage() : "")); } else { - JmeSystem.showErrorDialog(errMsg); + JmeSystem.handleErrorMessage(errMsg); } } @@ -706,40 +706,12 @@ public void start() { logger.config("VR mode disabled."); // not in VR, show settings dialog - if( Platform.get() != Platform.MACOSX ) { - if (!JmeSystem.showSettingsDialog(settings, loadSettings)) { - logger.config("Starting application [SUCCESS]"); - return; - } - } else { - // GLFW workaround on macs - settings.setFrequency(defDev.getDisplayMode().getRefreshRate()); - settings.setDepthBits(24); - settings.setVSync(true); - // try and read resolution from file in local dir - File resFile = new File("resolution.txt"); - if( resFile.exists() ) { - try { - BufferedReader br = new BufferedReader(new FileReader(resFile)); - settings.setWidth(Integer.parseInt(br.readLine())); - settings.setHeight(Integer.parseInt(br.readLine())); - try { - settings.setFullscreen(br.readLine().toLowerCase(Locale.ENGLISH).contains("full")); - } catch(Exception e) { - settings.setFullscreen(false); - } - br.close(); - } catch(Exception e) { - settings.setWidth(1280); - settings.setHeight(720); - } - } else { - settings.setWidth(1280); - settings.setHeight(720); - settings.setFullscreen(false); - } - settings.setResizable(false); + if (!JmeSystem.showSettingsDialog(settings, loadSettings)) { + logger.config("Starting application [SUCCESS]"); + return; } + + settings.setSwapBuffers(true); } else { logger.config("VR mode enabled."); diff --git a/settings.gradle b/settings.gradle index 78341dfa85..3f6acceb8e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -33,6 +33,7 @@ include 'jme3-testdata' // Example projects include 'jme3-examples' +include 'jme3-awt-dialogs' if(buildAndroidExamples == "true"){ include 'jme3-android-examples'