From a69dca413270e5e613a454e2ec07455ecd265bb7 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 16 Aug 2015 01:15:42 +0200 Subject: [PATCH] WIP adding JavaFX utils/classes --- Dorkbox-Util/src/dorkbox/util/JavaFxUtil.java | 85 ++++++++ Dorkbox-Util/src/dorkbox/util/ScreenUtil.java | 79 +++++++ Dorkbox-Util/src/dorkbox/util/SwingUtil.java | 69 +------ .../src/dorkbox/util/javafx/Wizard.java | 195 +++++++++++++++--- .../src/dorkbox/util/javafx/WizardPage.java | 14 -- 5 files changed, 333 insertions(+), 109 deletions(-) create mode 100644 Dorkbox-Util/src/dorkbox/util/JavaFxUtil.java create mode 100644 Dorkbox-Util/src/dorkbox/util/ScreenUtil.java diff --git a/Dorkbox-Util/src/dorkbox/util/JavaFxUtil.java b/Dorkbox-Util/src/dorkbox/util/JavaFxUtil.java new file mode 100644 index 0000000..8637322 --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/JavaFxUtil.java @@ -0,0 +1,85 @@ +package dorkbox.util; + +import javafx.application.Platform; + +import java.awt.*; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; + +/** + * + */ +public +class JavaFxUtil { + + public static final javafx.scene.text.Font DEFAULT_FONT = new javafx.scene.text.Font(13); + + public static + void showOnSameScreenAsMouseCenter(javafx.stage.Window stage) { + Point mouseLocation = MouseInfo.getPointerInfo() + .getLocation(); + + GraphicsDevice deviceAtMouse = ScreenUtil.getGraphicsDeviceAt(mouseLocation); + Rectangle bounds = deviceAtMouse.getDefaultConfiguration() + .getBounds(); + + stage.setX(bounds.x + bounds.width / 2 - stage.getWidth() / 2); + stage.setY(bounds.y + bounds.height / 2 - stage.getHeight() / 2); + } + + public static + void showOnSameScreenAsMouse(javafx.stage.Window stage) { + Point mouseLocation = MouseInfo.getPointerInfo() + .getLocation(); + + GraphicsDevice deviceAtMouse = ScreenUtil.getGraphicsDeviceAt(mouseLocation); + + stage.setX(deviceAtMouse.getDefaultConfiguration() + .getBounds().x); + } + + public static void runAndWait(Runnable runnable) { + // run synchronously on JavaFX thread + if (Platform.isFxApplicationThread()) { + runnable.run(); + return; + } + + FutureTask future = new FutureTask(runnable, null); + Platform.runLater(future); + try { + future.get(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } + + public static T runAndWait(Callable callable) { + // run synchronously on JavaFX thread + if (Platform.isFxApplicationThread()) { + try { + return (T) callable.call(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + FutureTask future = new FutureTask(callable); + Platform.runLater(future); + try { + return (T) future.get(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + return null; + } + + public static void runLater(Runnable runnable) { + Platform.runLater(runnable); + } +} diff --git a/Dorkbox-Util/src/dorkbox/util/ScreenUtil.java b/Dorkbox-Util/src/dorkbox/util/ScreenUtil.java new file mode 100644 index 0000000..2e9ccf5 --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/ScreenUtil.java @@ -0,0 +1,79 @@ +package dorkbox.util; + +import java.awt.*; +import java.util.ArrayList; + +/** + * Screen utilities + */ +public final +class ScreenUtil { + public static + Rectangle getScreenBoundsAt(Point pos) { + GraphicsDevice gd = getGraphicsDeviceAt(pos); + Rectangle bounds = null; + + if (gd != null) { + bounds = gd.getDefaultConfiguration() + .getBounds(); + } + + return bounds; + } + + public static + GraphicsDevice getGraphicsDeviceAt(Point pos) { + GraphicsDevice device; + + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice lstGDs[] = ge.getScreenDevices(); + + ArrayList lstDevices = new ArrayList(lstGDs.length); + + for (GraphicsDevice gd : lstGDs) { + + GraphicsConfiguration gc = gd.getDefaultConfiguration(); + Rectangle screenBounds = gc.getBounds(); + + if (screenBounds.contains(pos)) { + lstDevices.add(gd); + } + } + + if (lstDevices.size() > 0) { + device = lstDevices.get(0); + } + else { + device = ge.getDefaultScreenDevice(); + } + + return device; + } + + private + ScreenUtil() { + } + + +// public static Rectangle getSafeScreenBounds(Point pos) { +// Rectangle bounds = getScreenBoundsAt(pos); +// Insets insets = getScreenInsetsAt(pos); +// +// bounds.x += insets.left; +// bounds.y += insets.top; +// bounds.width -= insets.left + insets.right; +// bounds.height -= insets.top + insets.bottom; +// +// return bounds; +// } + +// public static Insets getScreenInsetsAt(Point pos) { +// GraphicsDevice gd = getGraphicsDeviceAt(pos); +// Insets insets = null; +// if (gd != null) { +// insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.getDefaultConfiguration()); +// } +// +// return insets; +// } +} diff --git a/Dorkbox-Util/src/dorkbox/util/SwingUtil.java b/Dorkbox-Util/src/dorkbox/util/SwingUtil.java index 1dd5d2f..31ddcfa 100644 --- a/Dorkbox-Util/src/dorkbox/util/SwingUtil.java +++ b/Dorkbox-Util/src/dorkbox/util/SwingUtil.java @@ -18,7 +18,6 @@ package dorkbox.util; import javax.swing.*; import java.awt.*; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; public class SwingUtil { @@ -27,7 +26,7 @@ class SwingUtil { Point mouseLocation = MouseInfo.getPointerInfo() .getLocation(); - GraphicsDevice deviceAtMouse = getGraphicsDeviceAt(mouseLocation); + GraphicsDevice deviceAtMouse = ScreenUtil.getGraphicsDeviceAt(mouseLocation); Rectangle bounds = deviceAtMouse.getDefaultConfiguration() .getBounds(); frame.setLocation(bounds.x + bounds.width / 2 - frame.getWidth() / 2, bounds.y + bounds.height / 2 - frame.getHeight() / 2); @@ -38,75 +37,11 @@ class SwingUtil { Point mouseLocation = MouseInfo.getPointerInfo() .getLocation(); - GraphicsDevice deviceAtMouse = getGraphicsDeviceAt(mouseLocation); + GraphicsDevice deviceAtMouse = ScreenUtil.getGraphicsDeviceAt(mouseLocation); frame.setLocation(deviceAtMouse.getDefaultConfiguration() .getBounds().x, frame.getY()); } - public static - Rectangle getScreenBoundsAt(Point pos) { - GraphicsDevice gd = SwingUtil.getGraphicsDeviceAt(pos); - Rectangle bounds = null; - if (gd != null) { - bounds = gd.getDefaultConfiguration() - .getBounds(); - } - - return bounds; - } - - public static - GraphicsDevice getGraphicsDeviceAt(Point pos) { - GraphicsDevice device; - - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice lstGDs[] = ge.getScreenDevices(); - - ArrayList lstDevices = new ArrayList(lstGDs.length); - - for (GraphicsDevice gd : lstGDs) { - - GraphicsConfiguration gc = gd.getDefaultConfiguration(); - Rectangle screenBounds = gc.getBounds(); - - if (screenBounds.contains(pos)) { - lstDevices.add(gd); - } - } - - if (lstDevices.size() > 0) { - device = lstDevices.get(0); - } - else { - device = ge.getDefaultScreenDevice(); - } - - return device; - } - - -// public static Rectangle getSafeScreenBounds(Point pos) { -// Rectangle bounds = getScreenBoundsAt(pos); -// Insets insets = getScreenInsetsAt(pos); -// -// bounds.x += insets.left; -// bounds.y += insets.top; -// bounds.width -= insets.left + insets.right; -// bounds.height -= insets.top + insets.bottom; -// -// return bounds; -// } - -// public static Insets getScreenInsetsAt(Point pos) { -// GraphicsDevice gd = getGraphicsDeviceAt(pos); -// Insets insets = null; -// if (gd != null) { -// insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.getDefaultConfiguration()); -// } -// -// return insets; -// } - public static void invokeLater(Runnable runnable) { if (EventQueue.isDispatchThread()) { diff --git a/Dorkbox-Util/src/dorkbox/util/javafx/Wizard.java b/Dorkbox-Util/src/dorkbox/util/javafx/Wizard.java index 22afdfd..67c83f7 100644 --- a/Dorkbox-Util/src/dorkbox/util/javafx/Wizard.java +++ b/Dorkbox-Util/src/dorkbox/util/javafx/Wizard.java @@ -30,10 +30,16 @@ package dorkbox.util.javafx; import dorkbox.util.JavaFxUtil; +import dorkbox.util.ScreenUtil; import impl.org.controlsfx.ImplUtils; -import javafx.application.Platform; +import javafx.animation.Interpolator; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; import javafx.beans.property.*; +import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; +import javafx.beans.value.WritableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.ObservableMap; @@ -46,19 +52,25 @@ import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Dialog; import javafx.scene.control.ToolBar; +import javafx.scene.image.Image; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.*; import javafx.scene.text.Font; import javafx.scene.text.Text; +import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.stage.Window; +import javafx.util.Duration; import org.controlsfx.control.PopOver; import org.controlsfx.tools.ValueExtractor; import org.controlsfx.validation.ValidationSupport; +import java.awt.*; import java.util.*; +import java.util.List; import java.util.function.BooleanSupplier; import java.util.function.Consumer; @@ -118,8 +130,9 @@ import java.util.function.Consumer; * } * };} */ +@SuppressWarnings("unused") public class Wizard { - final Stage stage = new Stage(StageStyle.UTILITY); + final Stage stage = new Stage(StageStyle.UNDECORATED); private final Text headerText; private final VBox center; @@ -174,7 +187,17 @@ public class Wizard { private final Font defaultHeaderFont; private VBox graphicRegion; + WritableValue writableHeight = new WritableValue() { + @Override + public Double getValue() { + return stage.getHeight(); + } + @Override + public void setValue(Double value) { + stage.setHeight(value); + } + }; /************************************************************************** * @@ -209,6 +232,11 @@ public class Wizard { */ public Wizard(Object owner, String title) { + stage.initModality(Modality.APPLICATION_MODAL); + stage.titleProperty() + .bind(this.titleProperty); + setTitle(title); + BUTTON_PREVIOUS.setDisable(true); BUTTON_NEXT.setDisable(true); @@ -229,9 +257,6 @@ public class Wizard { }); - stage.titleProperty() - .bind(this.titleProperty); - setTitle(title); popOver = new PopOver(); popOver.setDetachable(false); @@ -264,7 +289,7 @@ public class Wizard { public void accept(final WizardPage currentPage) { if (currentPage.autoFocusNext) { - Platform.runLater(BUTTON_NEXT::requestFocus); + JavaFxUtil.runLater(BUTTON_NEXT::requestFocus); } } }; @@ -285,7 +310,6 @@ public class Wizard { Button cancel = new Button("Cancel"); cancel.addEventFilter(ActionEvent.ACTION, event -> { - event.consume(); success = false; stage.close(); }); @@ -314,13 +338,25 @@ public class Wizard { // center.setStyle("-fx-background-color: #2046ff;"); borderPane.setCenter(center); - Scene scene2 = new Scene(borderPane); - stage.setScene(scene2); + + Scene scene = new Scene(borderPane); + stage.setScene(scene); stage.setMinWidth(300); stage.setMinHeight(140); stage.setWidth(300); stage.setHeight(140); + + scene.setOnKeyPressed(new EventHandler() { + public void handle(KeyEvent ke) { + if (ke.getCode() == KeyCode.ESCAPE) { + success = false; + stage.close(); + } + } + }); + + Window window = null; if (owner instanceof Window) { window = (Window) owner; @@ -331,24 +367,115 @@ public class Wizard { } stage.initOwner(window); + + stage.setResizable(false); // hide the minimize/maximize decorations + stage.showingProperty().addListener(new ChangeListener() { + @Override + public + void changed(final ObservableValue observable, final Boolean oldValue, final Boolean newValue) { + if (newValue) { + JavaFxUtil.runLater(() -> { + // If this runs now, it will bug out, and flash on the screen before we want it to. + // REALLY dumb, but we have to wait for the system to draw the window and finish BEFORE we move it + // otherwise, it'll 'flash' onscreen because it will still be in the middle of it's initial "on-show" fade-in. + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + Point mouseLocation = MouseInfo.getPointerInfo() + .getLocation(); + + final GraphicsDevice deviceAtMouse = ScreenUtil.getGraphicsDeviceAt(mouseLocation); + final Rectangle bounds = deviceAtMouse.getDefaultConfiguration() + .getBounds(); + + final double stageX = bounds.x + bounds.width / 2 - stage.getWidth() / 2; + final double stageY = bounds.y + bounds.height / 2 - stage.getHeight() / 2; + + stage.setX(stageX); + stage.setY(stageY); + + + Timeline timeline = new Timeline(); + timeline.setCycleCount(1); + timeline.getKeyFrames() + .addAll(new KeyFrame(Duration.millis(700), + new KeyValue(stage.opacityProperty(), 1, Interpolator.EASE_OUT))); + + + timeline.setOnFinished(event -> { + final double verifyStageX = bounds.x + bounds.width / 2 - stage.getWidth() / 2; + final double verifyStageY = bounds.y + bounds.height / 2 - stage.getHeight() / 2; + + if (stageX != verifyStageX || stageY != verifyStageY) { + // whoops. we should move the stage to the correct location. + + final DoubleProperty xProp = new SimpleDoubleProperty(); + xProp.addListener((observable1, oldValue1, newValue1) -> { + stage.setX((Double) newValue1); + }); + + final DoubleProperty yProp = new SimpleDoubleProperty(); + yProp.addListener((observable1, oldValue1, newValue1) -> { + stage.setY((Double) newValue1); + }); + + Timeline timelineMove = new Timeline(); + timelineMove.getKeyFrames() + .addAll(new KeyFrame(Duration.ZERO, + new KeyValue(xProp, stageX), + new KeyValue(yProp, stageY)), + new KeyFrame(Duration.millis(300), + new KeyValue(xProp, verifyStageX, Interpolator.EASE_OUT), + new KeyValue(yProp, verifyStageY, Interpolator.EASE_OUT))); + + timelineMove.play(); + } + + // when the mouse enters or leaves, we want to fade in/out the application + Timeline timelineVis = new Timeline(); + scene.addEventHandler(MouseEvent.MOUSE_ENTERED, event1 -> { + timelineVis.stop(); + timelineVis.getKeyFrames().clear(); + timelineVis.getKeyFrames() + .addAll(new KeyFrame(Duration.millis(700), + new KeyValue(stage.opacityProperty(), 1, Interpolator.EASE_OUT))); + timelineVis.play(); + }); + + scene.addEventHandler(MouseEvent.MOUSE_EXITED, event1 -> { + timelineVis.stop(); + timelineVis.getKeyFrames().clear(); + timelineVis.getKeyFrames() + .addAll(new KeyFrame(Duration.millis(700), + new KeyValue(stage.opacityProperty(), .7, Interpolator.EASE_OUT))); + timelineVis.play(); + }); + }); + + timeline.play(); + }); + } + } + }); } private void validatePopover(final String newValue) { if (newValue != null) { - currentPage.ifPresent(currentPage -> { - Platform.runLater(() -> { - final PopOver popOver = this.popOver; + currentPage.ifPresent(currentPage -> JavaFxUtil.runLater(() -> { + final PopOver popOver = this.popOver; - this.popOverErrorText.setText(newValue); + this.popOverErrorText.setText(newValue); - if (!popOver.isShowing()) { - popOver.setX(0); - popOver.setY(0); - popOver.show(BUTTON_NEXT, -10); - } - }); - }); + if (!popOver.isShowing()) { + popOver.setX(0); + popOver.setY(0); + popOver.show(BUTTON_NEXT, -10); + } + })); } else { popOver.hide(); } @@ -377,8 +504,7 @@ public class Wizard { */ public final void show() { - JavaFxUtil.showOnSameScreenAsMouseCenter(stage); - stage.show(); +// stage.show(); } /** @@ -389,7 +515,9 @@ public class Wizard { */ public final boolean showAndWait() { - JavaFxUtil.showOnSameScreenAsMouseCenter(stage); + stage.setOpacity(0.0); + stage.setX(10000); + stage.setY(10000); stage.showAndWait(); return success; @@ -692,21 +820,21 @@ public class Wizard { final Node firstFocusElement = currentPage.firstFocusElement; if (firstFocusElement != null) { - Platform.runLater(() -> { + JavaFxUtil.runLater(() -> { if (isInvalid()) { firstFocusElement.requestFocus(); } else { - Platform.runLater(BUTTON_NEXT::requestFocus); + JavaFxUtil.runLater(BUTTON_NEXT::requestFocus); } }); } else { if (isInvalid()) { - Platform.runLater(BUTTON_PREVIOUS::requestFocus); + JavaFxUtil.runLater(BUTTON_PREVIOUS::requestFocus); } else { - Platform.runLater(BUTTON_NEXT::requestFocus); + JavaFxUtil.runLater(BUTTON_NEXT::requestFocus); } } @@ -735,7 +863,7 @@ public class Wizard { stage.sizeToScene(); } - Platform.runLater(() -> { + JavaFxUtil.runLater(() -> { if (isInvalid()) { validatePopover(currentPage.invalidPropertyStrings.get()); } else { @@ -863,6 +991,17 @@ public class Wizard { BUTTON_PREVIOUS.requestFocus(); } + + +// You can add multiple images of different sizes and JavaFX will pick the one that fits best. Because you have different sizes in task bar and different in title bar. + public + void setApplicationIcon(final Image applicationIcon) { + final ObservableList icons = stage.getIcons(); + icons.clear(); + icons.add(applicationIcon); + } + + /************************************************************************** * * Support classes diff --git a/Dorkbox-Util/src/dorkbox/util/javafx/WizardPage.java b/Dorkbox-Util/src/dorkbox/util/javafx/WizardPage.java index 5f064d7..fdb38d0 100644 --- a/Dorkbox-Util/src/dorkbox/util/javafx/WizardPage.java +++ b/Dorkbox-Util/src/dorkbox/util/javafx/WizardPage.java @@ -139,20 +139,6 @@ class WizardPage { public void registerValidator(final Control control, final Validator validator) { - Platform.runLater(() -> { - validator.apply(control, null); -// Optional odecorator = Optional.ofNullable(validationSupport.getValidationDecorator()); -// for (Control target : validationSupport.getRegisteredControls()) { -// odecorator.ifPresent(decorator -> { -// decorator.removeDecorations(target); -// decorator.applyRequiredDecoration(target); -// validationResults.get(target); -// Optional highestMessage = validationSupport.getHighestMessage(target); -// highestMessage.ifPresent(msg -> decorator.applyValidationDecoration(msg)); -// }); -// } - }); - this.validationSupport.registerValidator(control, validator); }