Missing Mac APIs in JavaFX

Dr. Michael Paus mp at jugs.org
Sun Nov 8 08:09:46 UTC 2015


Hi,

I agree with you that these things should work as expected with JavaFX. 
Have you filed a bug report for them?
In addition to the platform specific APIs I would prefer a common 
solution for common things though, like opening
a file which is associated with your application.

Michael

Am 04.11.15 um 04:41 schrieb Neil Brown:
> Hi,
>
> Short version: I believe JavaFX is lacking some Mac-related APIs which 
> cannot be worked around without an internal com.sun.* call, and would 
> like to request that the APIs be added/made public for JDK 9, perhaps 
> as part of Jonathan Giles' work around JEP 253.
>
> I stand to be corrected on any of this...
>
> Long version: On Windows and Linux, files are generally opened via 
> command-line arguments.  On Mac, there is a file-open event issued to 
> an application, e.g. you double-click a .txt file, TextEdit gets a 
> file-open event.  If TextEdit is not open, it will be launched, then a 
> file-open event will be sent to it.
>
> Pre-JavaFX, the Java solution to receive these events was to use the 
> com.apple.eawt.* packages, which Oracle added in JDK 7. However, it 
> seems that these APIs don't play nice with FX.
>
> I've put some source code at the end of the email for a simple 
> application which I bundled with infinitekind's appbundler fork for 
> testing (https://bitbucket.org/infinitekind/appbundler/).  No matter 
> where you uncomment the setEAWTFileHandler call (search AAA, or BBB in 
> the source), the .app always misses the initial load event which 
> caused the application to open.  The net result is if the user 
> double-clicks an associated file, the JavaFX application will launch, 
> but not load the file in question unless the user double-clicks it 
> again.  Needless to say, this is problematic.
>
> If you look at the FX source code, you can see that FX does set a 
> file-open event handler with an empty implementation 
> (com.sun.glass.ui.Application, line 83 in 8u60).  Grepping the source 
> code shows this method is not implemented anywhere in the source code, 
> so FX is simply discarding the event.  The only Java way I've found to 
> let an FX application listen to the open files event is using a 
> com.sun call in the Application constructor (see CCC in the source, 
> and setFXFileHandler).  Suggestions for a better work-around very 
> welcome!
>
>
> The second API is a similar problem.  JavaFX installs an application 
> menu with standard options, including "Quit" (bound to Cmd-Q).  If you 
> leave Platform.setImplicitExit to its default value of true, all is 
> well.  Cmd-Q quits the application. However, if you 
> setImplicitExit(false) (see YYY in the source), as our actual 
> application must, problems arise.  The quit handler now is a no-op, 
> with no way of overriding it.  Pressing Cmd-Q or right-clicking the 
> dock and selecting quit is now totally ignored by the application, and 
> from the user's perspective the app is unquittable by these normal 
> means.  The only work-around I've found is to set a quit handler in 
> the same spot we set the file open handler (see ZZZ in the source: 
> I've used Platform.exit for this example, in reality we have a nicer 
> custom shutdown routine).
>
>
> My proposal is fairly straight-forward: the 
> com.sun.glass.ui.Application.EventHandler class (and associated 
> setEventHandler call) needs to become a public API in JDK 9, or else 
> these issues will be impossible to work around post-modularisation. 
> Initial file open and quit handler are the only ones biting us at the 
> moment, but I suspect that whole handler class should be public, 
> either as-is or by repurposing it into an EventHandler with some sort 
> of AppEvent extends Event class.
>
> Thanks,
>
> Neil.
>
> ======
> src/example/OpenApp.java
> ======
> package example;
>
> import java.io.File;
> import java.util.ArrayList;
> import java.util.Arrays;
> import java.util.List;
> import java.util.stream.Collectors;
> import javafx.application.Application;
> import javafx.application.Platform;
> import javafx.collections.FXCollections;
> import javafx.collections.ListChangeListener;
> import javafx.collections.ObservableList;
> import javafx.scene.Scene;
> import javafx.scene.control.Label;
> import javafx.scene.control.Menu;
> import javafx.scene.control.MenuBar;
> import javafx.scene.layout.BorderPane;
> import javafx.stage.Stage;
>
> public class OpenApp extends Application
> {
>     private static ObservableList<File> requestedFiles = 
> FXCollections.observableArrayList();
>
>     public static void main(String[] args)
>     {
>         // AAA: Uncommenting here, we miss initial load events:
>         //setEAWTFileHandler();
>         launch(args);
>     }
>
>     @Override
>     public void start(Stage stage) throws Exception
>     {
>         // YYY: this causes problems.
>         //Platform.setImplicitExit(false);
>         // BBB: Here also misses initial load events:
>         //setEAWTFileHandler();
>
>         BorderPane bp = new BorderPane();
>         Label filename = new Label("");
>         bp.setCenter(filename);
>         Runnable updateFiles = () -> 
> filename.setText(requestedFiles.stream().map(File::getAbsolutePath).collect(Collectors.joining("\n")));
>         requestedFiles.addListener((ListChangeListener<? super File>)c 
> -> updateFiles.run());
>         updateFiles.run();
>         bp.setBottom(new Label("Example Application"));
>         MenuBar menuBar = new MenuBar(new Menu("My Custom Menu"));
>         menuBar.setUseSystemMenuBar(true);
>         bp.setTop(menuBar);
>         stage.setScene(new Scene(bp));
>         stage.show();
>     }
>
>     public OpenApp()
>     {
>         // CCC: Constructor is the only time we are at the right point in
>         // FX control flow.
>         //setFXFileHandler();
>     }
>
>     private static void setEAWTFileHandler()
>     {
>         com.apple.eawt.Application appleApp = 
> com.apple.eawt.Application.getApplication();
>         appleApp.setOpenFileHandler(e -> {
>             List<File> files = new ArrayList(e.getFiles());
>             Platform.runLater(() -> requestedFiles.addAll(e.getFiles()));
>         });
>     }
>
>     private static void setFXFileHandler()
>     {
>         com.sun.glass.ui.Application glassApp = 
> com.sun.glass.ui.Application.GetApplication();
>         glassApp.setEventHandler(new 
> com.sun.glass.ui.Application.EventHandler() {
>             @Override
>             public void 
> handleOpenFilesAction(com.sun.glass.ui.Application app, long time, 
> String[] filenames)
>             {
>                 List<File> files = 
> Arrays.stream(filenames).map(File::new).collect(Collectors.toList());
>                 Platform.runLater(() -> requestedFiles.addAll(files));
>                 super.handleOpenFilesAction(app, time, filenames);
>             }
>
>             // ZZZ: add our own quit handler, too
>             /*
>             @Override
>             public void handleQuitAction(com.sun.glass.ui.Application 
> app, long time)
>             {
>                 Platform.exit();
>                 super.handleQuitAction(app, time);
>             }
>             */
>         });
>     }
>
> }
>
> ======
> build.xml (needs appbundler in ant lib, built from 
> https://bitbucket.org/infinitekind/appbundler/)
> ======
> <?xml version="1.0" encoding="UTF-8"?>
> <project name="machandler" default="all">
>     <taskdef name="bundleapp"
>              classpath="appbundler-1.0ea.jar"
>              classname="com.oracle.appbundler.AppBundlerTask"/>
>
>     <target name="all">
>         <mkdir dir="classes"/>
>         <javac srcdir="src" destdir="classes">
>             <compilerarg value="-XDignore.symbol.file=true"/>
>         </javac>
>         <jar destfile="openapp.jar" basedir="classes"></jar>
>         <bundleapp
>                 jvmrequired="1.8"
>                 outputdirectory="."
>                 name="OpenApp"
>                 displayname="OpenApp"
>                 executableName="OpenApp"
>                 identifier="example.OpenApp"
>                 shortversion="1.0"
>                 version="1.0"
>                 mainclassname="example.OpenApp">
>             <classpath file="openapp.jar"/>
>             <bundledocument extensions="dummyfx"
>                             name="Dummy document"
>                             role="editor">
>             </bundledocument>
>         </bundleapp>
>     </target>
> </project>



More information about the openjfx-dev mailing list