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