From d3feb64237f583bec7ac63cd2a5d15db2515e499 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sun, 19 Jan 2020 16:24:16 -0500 Subject: [PATCH] introduce ShimAWT, moving selectInput/OutputFolder to PSurface, being AWT purge --- core/src/processing/awt/PSurfaceAWT.java | 36 +++ core/src/processing/awt/ShimAWT.java | 265 +++++++++++++++++++ core/src/processing/core/PApplet.java | 49 ++-- core/src/processing/core/PSurface.java | 14 + core/src/processing/core/PSurfaceNone.java | 26 ++ core/src/processing/javafx/PSurfaceFX.java | 23 ++ core/src/processing/opengl/PSurfaceJOGL.java | 72 ++++- core/todo.txt | 3 + 8 files changed, 453 insertions(+), 35 deletions(-) create mode 100644 core/src/processing/awt/ShimAWT.java diff --git a/core/src/processing/awt/PSurfaceAWT.java b/core/src/processing/awt/PSurfaceAWT.java index 8d897f44d..d26b7e4d8 100644 --- a/core/src/processing/awt/PSurfaceAWT.java +++ b/core/src/processing/awt/PSurfaceAWT.java @@ -41,6 +41,7 @@ import java.awt.Toolkit; import java.awt.event.*; import java.awt.geom.Rectangle2D; import java.awt.image.*; +import java.io.File; import java.lang.management.ManagementFactory; import java.lang.reflect.Method; import java.net.URL; @@ -356,6 +357,41 @@ public class PSurfaceAWT extends PSurfaceNone { */ + /* + @Override + public int displayDensity() { + return shim.displayDensity(); + } + + + @Override + public int displayDensity(int display) { + return shim.displayDensity(display); + } + */ + + + @Override + public void selectInput(String prompt, String callback, + File file, Object callbackObject) { + ShimAWT.selectInput(prompt, callback, file, callbackObject); + } + + + @Override + public void selectOutput(String prompt, String callback, + File file, Object callbackObject) { + ShimAWT.selectOutput(prompt, callback, file, callbackObject); + } + + + @Override + public void selectFolder(String prompt, String callback, + File file, Object callbackObject) { + ShimAWT.selectFolder(prompt, callback, file, callbackObject); + } + + // what needs to happen here? @Override public void initOffscreen(PApplet sketch) { diff --git a/core/src/processing/awt/ShimAWT.java b/core/src/processing/awt/ShimAWT.java new file mode 100644 index 000000000..bd8a9a45d --- /dev/null +++ b/core/src/processing/awt/ShimAWT.java @@ -0,0 +1,265 @@ +package processing.awt; + +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Frame; +import java.io.File; + +import javax.swing.JFileChooser; +import javax.swing.UIManager; + +import processing.core.PApplet; +import processing.core.PConstants; + + +/** + * This class exists as an abstraction layer to remove AWT from PApplet. + * It is a staging area for AWT-specific code that's shared by the Java2D, + * JavaFX, and JOGL renderers. Once PSurfaceFX and PSurfaceJOGL have + * their own implementations, these methods will move to PSurfaceAWT. + */ +public class ShimAWT { + /* + PGraphics graphics; + PApplet sketch; + + + public ShimAWT(PApplet sketch) { + this.graphics = graphics; + this.sketch = sketch; + } + */ + + + /* + public int displayDensity() { + if (sketch.display != PConstants.SPAN && (sketch.fullScreen || sketch.present)) { + return displayDensity(sketch.display); + } + // walk through all displays, use 2 if any display is 2 + for (int i = 0; i < displayDevices.length; i++) { + if (displayDensity(i+1) == 2) { + return 2; + } + } + // If nobody's density is 2 then everyone is 1 + return 1; + } + */ + + + /** + * @param display the display number to check + * (1-indexed to match the Preferences dialog box) + */ + /* + public int displayDensity(int display) { + if (display > 0 && display <= displayDevices.length) { + GraphicsConfiguration graphicsConfig = + displayDevices[display - 1].getDefaultConfiguration(); + AffineTransform tx = graphicsConfig.getDefaultTransform(); + return (int) Math.round(tx.getScaleX()); + } + + System.err.println("Display " + display + " does not exist, returning "); + return 1; // not the end of the world, so don't throw a RuntimeException + } + */ + + + static public void selectInput(String prompt, String callbackMethod, + File file, Object callbackObject) { + EventQueue.invokeLater(() -> { + selectImpl(prompt, callbackMethod, file, + callbackObject, null, FileDialog.LOAD); + }); + } + + + /* + static public void selectOutput(String prompt, String callbackMethod, + File file, Object callbackObject, Frame parent) { + selectImpl(prompt, callbackMethod, file, callbackObject, parent, FileDialog.SAVE, null); + } + + + static public void selectOutput(String prompt, String callbackMethod, + File file, Object callbackObject, Frame parent, + PApplet sketch) { + selectImpl(prompt, callbackMethod, file, callbackObject, parent, FileDialog.SAVE, sketch); + } + */ + + + static public void selectOutput(String prompt, String callbackMethod, + File file, Object callbackObject) { + EventQueue.invokeLater(() -> { + selectImpl(prompt, callbackMethod, file, + callbackObject, null, FileDialog.SAVE); + }); + } + + + /* + // Will remove the 'sketch' parameter once we get an upstream JOGL fix + // https://github.com/processing/processing/issues/3831 + static protected void selectEvent(final String prompt, + final String callbackMethod, + final File defaultSelection, + final Object callbackObject, + final Frame parentFrame, + final int mode, + final PApplet sketch) { + EventQueue.invokeLater(new Runnable() { + public void run() { + boolean hide = (sketch != null) && + (sketch.g instanceof PGraphicsOpenGL) && + (PApplet.platform == PConstants.WINDOWS); + if (hide) sketch.getSurface().setVisible(false); + + selectImpl(prompt, callbackMethod, defaultSelection, callbackObject, + parentFrame, mode, sketch); + + if (hide) sketch.getSurface().setVisible(true); + } + }); + } + */ + + + static public void selectImpl(final String prompt, + final String callbackMethod, + final File defaultSelection, + final Object callbackObject, + final Frame parentFrame, + final int mode) { + File selectedFile = null; + + if (PApplet.useNativeSelect) { + FileDialog dialog = new FileDialog(parentFrame, prompt, mode); + if (defaultSelection != null) { + dialog.setDirectory(defaultSelection.getParent()); + dialog.setFile(defaultSelection.getName()); + } + + dialog.setVisible(true); + String directory = dialog.getDirectory(); + String filename = dialog.getFile(); + if (filename != null) { + selectedFile = new File(directory, filename); + } + + } else { + JFileChooser chooser = new JFileChooser(); + chooser.setDialogTitle(prompt); + if (defaultSelection != null) { + chooser.setSelectedFile(defaultSelection); + } + + int result = -1; + if (mode == FileDialog.SAVE) { + result = chooser.showSaveDialog(parentFrame); + } else if (mode == FileDialog.LOAD) { + result = chooser.showOpenDialog(parentFrame); + } + if (result == JFileChooser.APPROVE_OPTION) { + selectedFile = chooser.getSelectedFile(); + } + } + PApplet.selectCallback(selectedFile, callbackMethod, callbackObject); + } + + + static public void selectFolder(final String prompt, + final String callbackMethod, + final File defaultSelection, + final Object callbackObject) { + EventQueue.invokeLater(() -> { + selectFolderImpl(prompt, callbackMethod, defaultSelection, + callbackObject, null); + }); + } + + + /* + static public void selectFolder(final String prompt, + final String callbackMethod, + final File defaultSelection, + final Object callbackObject, + final Frame parentFrame) { + selectFolderEvent(prompt, callbackMethod, defaultSelection, callbackObject, parentFrame, null); + } + + + // Will remove the 'sketch' parameter once we get an upstream JOGL fix + // https://github.com/processing/processing/issues/3831 + static public void selectFolderEvent(final String prompt, + final String callbackMethod, + final File defaultSelection, + final Object callbackObject, + final Frame parentFrame, + final PApplet sketch) { + EventQueue.invokeLater(() -> { + selectFolderImpl(prompt, callbackMethod, defaultSelection, + callbackObject, parentFrame, sketch); + }); + } + */ + + + static public void selectFolderImpl(final String prompt, + final String callbackMethod, + final File defaultSelection, + final Object callbackObject, + final Frame parentFrame) { + File selectedFile = null; + if (PApplet.platform == PConstants.MACOS && PApplet.useNativeSelect) { + FileDialog fileDialog = + new FileDialog(parentFrame, prompt, FileDialog.LOAD); + if (defaultSelection != null) { + fileDialog.setDirectory(defaultSelection.getAbsolutePath()); + } + System.setProperty("apple.awt.fileDialogForDirectories", "true"); + fileDialog.setVisible(true); + System.setProperty("apple.awt.fileDialogForDirectories", "false"); + String filename = fileDialog.getFile(); + if (filename != null) { + selectedFile = new File(fileDialog.getDirectory(), fileDialog.getFile()); + } + } else { + checkLookAndFeel(); + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle(prompt); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + if (defaultSelection != null) { + fileChooser.setCurrentDirectory(defaultSelection); + } + + int result = fileChooser.showOpenDialog(parentFrame); + if (result == JFileChooser.APPROVE_OPTION) { + selectedFile = fileChooser.getSelectedFile(); + } + } + PApplet.selectCallback(selectedFile, callbackMethod, callbackObject); + } + + + static private boolean lookAndFeelCheck; + + /** + * Initialize the Look & Feel if it hasn't been already. + * Call this before using any Swing-related code in PApplet methods. + */ + static private void checkLookAndFeel() { + if (!lookAndFeelCheck) { + if (PApplet.platform == PConstants.WINDOWS) { + // Windows is defaulting to Metal or something else awful. + // Which also is not scaled properly with HiDPI interfaces. + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { } + } + lookAndFeelCheck = true; + } + } +} \ No newline at end of file diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 9cd0071d5..3e888c1b9 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -47,14 +47,6 @@ import javax.imageio.ImageIO; // allows us to remove our own MediaTracker code import javax.swing.ImageIcon; -// used by selectInput(), selectOutput(), selectFolder() -import java.awt.EventQueue; -import java.awt.FileDialog; -import javax.swing.JFileChooser; - -// set the look and feel, if specified -import javax.swing.UIManager; - // used by link() import java.awt.Desktop; @@ -6317,25 +6309,6 @@ public class PApplet implements PConstants { */ - static private boolean lookAndFeelCheck; - - /** - * Initialize the Look & Feel if it hasn't been already. - * Call this before using any Swing-related code in PApplet methods. - */ - static private void checkLookAndFeel() { - if (!lookAndFeelCheck) { - if (platform == WINDOWS) { - // Windows is defaulting to Metal or something else awful. - // Which also is not scaled properly with HiDPI interfaces. - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception e) { } - } - lookAndFeelCheck = true; - } - } - /** * Open a platform-specific file chooser dialog to select a file for input. * After the selection is made, the selected File will be passed to the @@ -6377,10 +6350,12 @@ public class PApplet implements PConstants { public void selectInput(String prompt, String callback, File file, Object callbackObject) { - selectInput(prompt, callback, file, callbackObject, null, this); //selectFrame()); + //selectInput(prompt, callback, file, callbackObject, null, this); + surface.selectInput(prompt, callback, file, callbackObject); } + /* static public void selectInput(String prompt, String callbackMethod, File file, Object callbackObject, Frame parent, PApplet sketch) { @@ -6392,6 +6367,7 @@ public class PApplet implements PConstants { File file, Object callbackObject, Frame parent) { selectImpl(prompt, callbackMethod, file, callbackObject, parent, FileDialog.LOAD, null); } + */ /** @@ -6413,10 +6389,12 @@ public class PApplet implements PConstants { public void selectOutput(String prompt, String callback, File file, Object callbackObject) { - selectOutput(prompt, callback, file, callbackObject, null, this); //selectFrame()); + //selectOutput(prompt, callback, file, callbackObject, null, this); + surface.selectOutput(prompt, callback, file, callbackObject); } + /* static public void selectOutput(String prompt, String callbackMethod, File file, Object callbackObject, Frame parent) { selectImpl(prompt, callbackMethod, file, callbackObject, parent, FileDialog.SAVE, null); @@ -6484,6 +6462,7 @@ public class PApplet implements PConstants { } }); } + */ /** @@ -6505,10 +6484,12 @@ public class PApplet implements PConstants { public void selectFolder(String prompt, String callback, File file, Object callbackObject) { - selectFolder(prompt, callback, file, callbackObject, null, this); //selectFrame()); + //selectFolder(prompt, callback, file, callbackObject, null, this); + surface.selectFolder(prompt, callback, file, callbackObject); } + /* static public void selectFolder(final String prompt, final String callbackMethod, final File defaultSelection, @@ -6567,11 +6548,12 @@ public class PApplet implements PConstants { } }); } + */ - static private void selectCallback(File selectedFile, - String callbackMethod, - Object callbackObject) { + static public void selectCallback(File selectedFile, + String callbackMethod, + Object callbackObject) { try { Class callbackClass = callbackObject.getClass(); Method selectMethod = @@ -6590,7 +6572,6 @@ public class PApplet implements PConstants { } - ////////////////////////////////////////////////////////////// // LISTING DIRECTORIES diff --git a/core/src/processing/core/PSurface.java b/core/src/processing/core/PSurface.java index c4f3580fb..3c9b8eb68 100644 --- a/core/src/processing/core/PSurface.java +++ b/core/src/processing/core/PSurface.java @@ -22,6 +22,7 @@ package processing.core; +import java.io.File; public interface PSurface { /** @@ -46,6 +47,19 @@ public interface PSurface { // int deviceIndex, boolean fullScreen, boolean spanDisplays); public void initFrame(PApplet sketch); +// public int displayDensity(); + +// public int displayDensity(int display); + + public void selectInput(String prompt, String callback, + File file, Object callbackObject); + + public void selectOutput(String prompt, String callback, + File file, Object callbackObject); + + public void selectFolder(String prompt, String callback, + File file, Object callbackObject); + /** * Get the native window object associated with this drawing surface. * For Java2D, this will be an AWT Frame object. For OpenGL, the window. diff --git a/core/src/processing/core/PSurfaceNone.java b/core/src/processing/core/PSurfaceNone.java index f86ac0cde..5367c385f 100644 --- a/core/src/processing/core/PSurfaceNone.java +++ b/core/src/processing/core/PSurfaceNone.java @@ -22,6 +22,7 @@ package processing.core; +import java.io.File; /** * Surface that's not really visible. Used for PDF and friends, or as a base @@ -44,6 +45,31 @@ public class PSurfaceNone implements PSurface { } + public int displayDensity() { + return 1; + } + + + public int displayDensity(int display) { + return 1; + } + + + public void selectInput(String prompt, String callback, File file, + Object callbackObject) { + } + + + public void selectOutput(String prompt, String callback, File file, + Object callbackObject) { + } + + + public void selectFolder(String prompt, String callback, File file, + Object callbackObject) { + } + + @Override public void initOffscreen(PApplet sketch) { this.sketch = sketch; diff --git a/core/src/processing/javafx/PSurfaceFX.java b/core/src/processing/javafx/PSurfaceFX.java index 831ef4418..98f85ff1c 100644 --- a/core/src/processing/javafx/PSurfaceFX.java +++ b/core/src/processing/javafx/PSurfaceFX.java @@ -27,6 +27,7 @@ import com.sun.glass.ui.Screen; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Rectangle; +import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; @@ -61,6 +62,7 @@ import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.stage.WindowEvent; import javafx.util.Duration; +import processing.awt.ShimAWT; import processing.core.*; @@ -222,6 +224,27 @@ public class PSurfaceFX implements PSurface { } + @Override + public void selectInput(String prompt, String callbackMethod, + File file, Object callbackObject) { + ShimAWT.selectInput(prompt, callbackMethod, file, callbackObject); + } + + + @Override + public void selectOutput(String prompt, String callbackMethod, + File file, Object callbackObject) { + ShimAWT.selectOutput(prompt, callbackMethod, file, callbackObject); + } + + + @Override + public void selectFolder(String prompt, String callbackMethod, + File file, Object callbackObject) { + ShimAWT.selectFolder(prompt, callbackMethod, file, callbackObject); + } + + public void initOffscreen(PApplet sketch) { } diff --git a/core/src/processing/opengl/PSurfaceJOGL.java b/core/src/processing/opengl/PSurfaceJOGL.java index 2b3896982..b52a3882e 100644 --- a/core/src/processing/opengl/PSurfaceJOGL.java +++ b/core/src/processing/opengl/PSurfaceJOGL.java @@ -25,6 +25,8 @@ package processing.opengl; import java.awt.Component; +import java.awt.EventQueue; +import java.awt.FileDialog; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; @@ -67,7 +69,7 @@ import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.util.FPSAnimator; - +import processing.awt.ShimAWT; import processing.core.PApplet; import processing.core.PConstants; import processing.core.PGraphics; @@ -81,6 +83,8 @@ public class PSurfaceJOGL implements PSurface { /** Selected GL profile */ public static GLProfile profile; + ShimAWT shim; + public PJOGL pgl; protected GLWindow window; @@ -118,6 +122,71 @@ public class PSurfaceJOGL implements PSurface { } + /* + @Override + public int displayDensity() { + return shim.displayDensity(); + } + + + @Override + public int displayDensity(int display) { + return shim.displayDensity(display); + } + */ + + + @Override + public void selectInput(String prompt, String callbackMethod, + File file, Object callbackObject) { + EventQueue.invokeLater(() -> { + // https://github.com/processing/processing/issues/3831 + boolean hide = (sketch != null) && + (PApplet.platform == PConstants.WINDOWS); + if (hide) setVisible(false); + + ShimAWT.selectImpl(prompt, callbackMethod, file, + callbackObject, null, FileDialog.LOAD); + + if (hide) setVisible(true); + }); + } + + + @Override + public void selectOutput(String prompt, String callbackMethod, + File file, Object callbackObject) { + EventQueue.invokeLater(() -> { + // https://github.com/processing/processing/issues/3831 + boolean hide = (sketch != null) && + (PApplet.platform == PConstants.WINDOWS); + if (hide) setVisible(false); + + ShimAWT.selectImpl(prompt, callbackMethod, file, + callbackObject, null, FileDialog.SAVE); + + if (hide) setVisible(true); + }); + } + + + @Override + public void selectFolder(String prompt, String callbackMethod, + File file, Object callbackObject) { + EventQueue.invokeLater(() -> { + // https://github.com/processing/processing/issues/3831 + boolean hide = (sketch != null) && + (PApplet.platform == PConstants.WINDOWS); + if (hide) setVisible(false); + + ShimAWT.selectFolderImpl(prompt, callbackMethod, file, + callbackObject, null); + + if (hide) setVisible(true); + }); + } + + public void initOffscreen(PApplet sketch) { this.sketch = sketch; @@ -134,6 +203,7 @@ public class PSurfaceJOGL implements PSurface { public void initFrame(PApplet sketch) { this.sketch = sketch; + initIcons(); initDisplay(); initGL(); diff --git a/core/todo.txt b/core/todo.txt index 26f0ad7d0..f3aaf5783 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -9,6 +9,7 @@ X show 'displays have separate spaces' notice in the console X allows us to get rid of JOptionPane X show 'displays have separate spaces' message when the param is unset X Catalina seems to have it un-set, but the default is the same +X move selectInput/Output/Folder to ShimAWT class _ static versions of selectInput/selectOutput/selectFolder in PApplet have been removed @@ -20,6 +21,8 @@ _ or even that it inits a surface-specific class for getting that info before final release +_ implement selectInput/Output/Folder methods in PSurfaceJOGL +_ implement selectInput/Output/Folder methods in PSurfaceFX _ Intel HD Graphics 3000 workaround is causing a big fat warning _ https://github.com/processing/processing4/issues/50 _ ThinkDifferent unavailable with --disable-awt, needs workaround