28

I'm using this to make a iOS-themed JavaFX2 (Java7) application with a frosted glass effect. The problem is that this code uses its effect on an ImageView. I'd like it to use its effect on whatever's behind the window, like this:

Is there anyway to do that? I'd also like that small drop-shadow effect you see around the above image.

To be clear, I don't want that slider or anything, just the effect of being able to see through the window and having that slight shadow around the edges. I want to use this iOS7-ish effect instead of aero, though.

This might be important: I'm using a modified version of Undecorator.

Community
  • 1
  • 1
Taconut
  • 951
  • 4
  • 10
  • 29

4 Answers4

26

frosty

import javafx.animation.*;
import javafx.application.*;
import javafx.beans.property.*;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.Label;
import javafx.scene.effect.*;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.image.*;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;

public class FrostyTech extends Application {

    private static final double BLUR_AMOUNT = 10;

    private static final Effect frostEffect =
        new BoxBlur(BLUR_AMOUNT, BLUR_AMOUNT, 3);

    private static final ImageView background = new ImageView();
    private static final StackPane layout = new StackPane();

    @Override public void start(Stage stage) {
        layout.getChildren().setAll(background, createContent());
        layout.setStyle("-fx-background-color: null");

        Scene scene = new Scene(
                layout,
                200, 300,
                Color.TRANSPARENT
        );

        Platform.setImplicitExit(false);

        scene.setOnMouseClicked(event -> {
                if (event.getClickCount() == 2) Platform.exit();
        });
        makeSmoke(stage);

        stage.initStyle(StageStyle.TRANSPARENT);
        stage.setScene(scene);
        stage.show();

        background.setImage(copyBackground(stage));
        background.setEffect(frostEffect);

        makeDraggable(stage, layout);
    }

    // copy a background node to be frozen over.
    private Image copyBackground(Stage stage) {
        final int X = (int) stage.getX();
        final int Y = (int) stage.getY();
        final int W = (int) stage.getWidth();
        final int H = (int) stage.getHeight();

        try {
            java.awt.Robot robot = new java.awt.Robot();
            java.awt.image.BufferedImage image = robot.createScreenCapture(new java.awt.Rectangle(X, Y, W, H));

            return SwingFXUtils.toFXImage(image, null);
        } catch (java.awt.AWTException e) {
            System.out.println("The robot of doom strikes!");
            e.printStackTrace();

            return null;
        }
    }

    // create some content to be displayed on top of the frozen glass panel.
    private Label createContent() {
        Label label = new Label("Create a new question for drop shadow effects.\n\nDrag to move\n\nDouble click to close");
        label.setPadding(new Insets(10));

        label.setStyle("-fx-font-size: 15px; -fx-text-fill: green;");
        label.setMaxWidth(250);
        label.setWrapText(true);

        return label;
    }

    // makes a stage draggable using a given node.
    public void makeDraggable(final Stage stage, final Node byNode) {
        final Delta dragDelta = new Delta();
        byNode.setOnMousePressed(mouseEvent -> {
            // record a delta distance for the drag and drop operation.
            dragDelta.x = stage.getX() - mouseEvent.getScreenX();
            dragDelta.y = stage.getY() - mouseEvent.getScreenY();
            byNode.setCursor(Cursor.MOVE);
        });
        final BooleanProperty inDrag = new SimpleBooleanProperty(false);

        byNode.setOnMouseReleased(mouseEvent -> {
            byNode.setCursor(Cursor.HAND);

            if (inDrag.get()) {
                stage.hide();

                Timeline pause = new Timeline(new KeyFrame(Duration.millis(50), event -> {
                    background.setImage(copyBackground(stage));
                    layout.getChildren().set(
                            0,
                            background
                    );
                    stage.show();
                }));
                pause.play();
            }

            inDrag.set(false);
        });
        byNode.setOnMouseDragged(mouseEvent -> {
            stage.setX(mouseEvent.getScreenX() + dragDelta.x);
            stage.setY(mouseEvent.getScreenY() + dragDelta.y);

            layout.getChildren().set(
                    0,
                    makeSmoke(stage)
            );

            inDrag.set(true);
        });
        byNode.setOnMouseEntered(mouseEvent -> {
            if (!mouseEvent.isPrimaryButtonDown()) {
                byNode.setCursor(Cursor.HAND);
            }
        });
        byNode.setOnMouseExited(mouseEvent -> {
            if (!mouseEvent.isPrimaryButtonDown()) {
                byNode.setCursor(Cursor.DEFAULT);
            }
        });
    }

    private javafx.scene.shape.Rectangle makeSmoke(Stage stage) {
        return new javafx.scene.shape.Rectangle(
                stage.getWidth(),
                stage.getHeight(),
                Color.WHITESMOKE.deriveColor(
                        0, 1, 1, 0.08
                )
        );
    }

    /** records relative x and y co-ordinates. */
    private static class Delta {
        double x, y;
    }

    public static void main(String[] args) {
        launch(args);
    }
}  

Related Questions

Community
  • 1
  • 1
jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • Lol. you did it again :D. Good thing I awarded you a couple seconds before the bounty ended. I was freaking out there. – Taconut Apr 09 '14 at 14:12
  • Hey! I made it so you could load any node on top of a FrostyPane and turned off blur when the window wasn't in focus (to make it always consistent). I have a couple questions: 1. I removed the bit of code that pauses everything for 50ms, and it still works fine. Is there something I'm missing? 2. Dragging the window restarts any functions I have going on. For example, my splash screen restarts when I move the window. Any way around this? Also, is there any way to easily implement resizing? I think I can handle it, but I just wanted to know if you knew of any shortcuts. – Taconut Apr 10 '14 at 22:49
  • 1
    Unfortunately, if the background changes, the glass effect won't redraw. Otherwise, looks great. – dejuknow Aug 26 '14 at 05:04
  • @jewelsea line 69 "The robot of doom strikes!" xaxaxaxa +100 – GOXR3PLUS Jun 09 '16 at 16:12
11

The visual effect that you want for OS dependent window decoration, can only be achieved through the APIs that OS provides. And thus was eliminated by StageStyle.TRANSPARENT below.

For JavaFX content itself, you can control the visuals of the stage > scene > root pane hierarchy. Stage and scene do not (and not aimed to) support advanced stylings so were eliminated by setting as transparent below.

@Override
public void start(Stage primaryStage) {
    StackPane root = new StackPane();
    root.setStyle("-fx-background-color: null;");
    root.setPadding(new Insets(10));

    DoubleProperty doubleProperty = new SimpleDoubleProperty(0);

    Region region = new Region();
    region.styleProperty().bind(Bindings
            .concat("-fx-background-radius:20; -fx-background-color: rgba(56, 176, 209, ")
            .concat(doubleProperty)
            .concat(");"));
    region.setEffect(new DropShadow(10, Color.GREY));

    Slider slider = new Slider(0, 1, .3);
    doubleProperty.bind(slider.valueProperty());

    root.getChildren().addAll(region, slider);

    primaryStage.initStyle(StageStyle.TRANSPARENT);
    Scene scene = new Scene(root, 300, 250);
    scene.setFill(Color.TRANSPARENT);

    primaryStage.setTitle("Hello World!");
    primaryStage.setScene(scene);
    primaryStage.show();
}

However the drop shadow effect does not play well with alpha value of the background color. You can observe it by changing the shadow's color to another contrast one.

Output:
enter image description here

jewelsea
  • 150,031
  • 14
  • 366
  • 406
Uluk Biy
  • 48,655
  • 13
  • 146
  • 153
4

To expand on Jewlsea's answer .. And using the above example with JavaFX ONLY ..

While the classes are not public API, it does avoid the AWT stack completely. Here is a non public example :

// copy a background node to be frozen over.
    private Image copyBackground(Stage stage) {
        final int X = (int) stage.getX();
        final int Y = (int) stage.getY();
        final int W = (int) stage.getWidth();
        final int H = (int) stage.getHeight();
        final Screen screen = Screen.getPrimary();
        try {

            Robot rbt = com.sun.glass.ui.Application.GetApplication().createRobot();
            Pixels p = rbt.getScreenCapture(
                (int)screen.getBounds().getMinX(),
                (int)screen.getBounds().getMinY(), 
                (int)screen.getBounds().getWidth(), 
                (int)screen.getBounds().getHeight(), 
                true
            );

            WritableImage dskTop = new WritableImage((int)screen.getBounds().getWidth(), (int)screen.getBounds().getHeight());
            dskTop.getPixelWriter().setPixels(
                (int)screen.getBounds().getMinX(),
                (int)screen.getBounds().getMinY(),
                (int)screen.getBounds().getWidth(),
                (int)screen.getBounds().getHeight(),
                PixelFormat.getByteBgraPreInstance(),
                p.asByteBuffer(), 
                (int)(screen.getBounds().getWidth() * 4)
            );

            WritableImage image = new WritableImage(W,H);
            image.getPixelWriter().setPixels(0, 0, W, H, dskTop.getPixelReader(), X, Y);

            return image;
        } catch (Exception e) {
            System.out.println("The robot of doom strikes!");
            e.printStackTrace();

            return null;
        }
    }

Results with a small dropshadow added:

    DropShadow shdw = new DropShadow();
    shdw.setBlurType(BlurType.GAUSSIAN);
    shdw.setColor(Color.GAINSBORO);
    shdw.setRadius(10);
    shdw.setSpread(0.12);
    shdw.setHeight(10);
    shdw.setWidth(10);
    layout.setEffect(shdw);

fxScreenCapture

jdub1581
  • 659
  • 5
  • 10
0

The opacity is a property of Node, which is the parent class in JavaFX for things that show up on the screen. http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html#opacityProperty

So you can just set the opacity on the object that you want to have fade away. You then have to add some sort of way to change the opacity on the desired object. Using the slider from your image is one way, but there are others.

Drop shadows can be done using the DropShadow effect... http://docs.oracle.com/javafx/2/api/javafx/scene/effect/DropShadow.html. I have never used it. This is a little high level but if there are follow up questions in the comments I can help answer them.

Jose Martinez
  • 11,452
  • 7
  • 53
  • 68
  • That picture is unrelated to the code I need. It just shows what I mean by "behind the window." What I meant was that I want the effect from [this answer](http://stackoverflow.com/questions/22622034/frosted-glass-effect-in-javafx/22630754#22630754) to be applied to whatever's behind the Java Window. – Taconut Apr 08 '14 at 20:49
  • Also, DropShadow will be applied behind the window. If the window's opaque, you'll be able to see the shadow behind it. – Taconut Apr 08 '14 at 20:50
  • Ah I see. So the object behind the Java window is not under the control of JavaFX, it is in fact just part of the OS's desktop? – Jose Martinez Apr 08 '14 at 21:00
  • Yup! I have no clue what to do! – Taconut Apr 08 '14 at 21:11
  • When i create javafx applications I get a window where my program displays in. I can do opacity and drop shadow on everything that is displayed within that window. But I do not have access to control the window's opacity itself nor visibility into what is behind my window. I would not go as far as say that it is not possible, as there are some very smart people out there, but it is outside my expertise. I will keep my eyes open for any solutions and report back. – Jose Martinez Apr 08 '14 at 21:31
  • 1
    You _can_ control the window's opacity. I've done it. It's just that it's tricky to add a drop shadow only around the edges. Also, that link I put works by applying effects to an image. I need to know how to do that on a dynamic background. – Taconut Apr 08 '14 at 21:55