1

I want to make a transparent stage, such as the below picture (windows 7)

that is windows 7 gadget window.. it looks like a fully glass

So I tried to make that form screencapture method.. but it was confusing...

Here is my code, hopefully it can explain the issue better:

import javafx.application.*;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.effect.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class Demo extends Application{
    private static final double BLUR_AMOUNT = 7;
    private static final Effect myBlur = new BoxBlur(BLUR_AMOUNT, BLUR_AMOUNT, 3);
    private static final ImageView background = new ImageView();
    private static final StackPane layout = new StackPane();

    private Button btn=new Button("hello");

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

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

        stage.initStyle(StageStyle.TRANSPARENT);
        stage.setScene(scene);
        stage.show();
        stage.setX(0);
        stage.setY(0);

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

        makeDraggable(stage);
    }

    private static class Delta {
        double x,y;
    }

    public void makeDraggable(final Stage stage) {
        final Delta dragDelta = new Delta();
        btn.setOnMousePressed(mouseEvent -> {
            dragDelta.x = stage.getX() - mouseEvent.getScreenX();
            dragDelta.y = stage.getY() - mouseEvent.getScreenY();    
        });

        btn.setOnMouseDragged(mouseEvent -> {
            stage.setX(mouseEvent.getScreenX() + dragDelta.x);
            stage.setY(mouseEvent.getScreenY() + dragDelta.y);
            btn.setOpacity(0.0);
            background.setImage(copyBackground(stage));
        });

         btn.setOnMouseReleased(mouseEvent -> {
            btn.setOpacity(1.0);
        });
    }

    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!");
            return null;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
Peter
  • 1,592
  • 13
  • 20

1 Answers1

1

The reason for the effect you are seeing is that the robot is taking a snapshot of a portion of the screen, including the window you have created. Moreover, this snapshot is happening after the stage's x and y properties have changed but before the window has actually moved (and therefore before the robot "sees" the effect of the changed properties). Thus you end up with the incorrect image being generated.

A simple fix is to snapshot the entire screen before you show the stage, and then to update the viewport applied to the image view when the stage moves (or changes size):

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.effect.BoxBlur;
import javafx.scene.effect.Effect;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class Demo extends Application{
    private static final double BLUR_AMOUNT = 7;
    private static final Effect myBlur = new BoxBlur(BLUR_AMOUNT, BLUR_AMOUNT, 3);
    private static final ImageView background = new ImageView();
    private static final StackPane layout = new StackPane();

    private Button btn=new Button("hello");

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

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

        stage.initStyle(StageStyle.TRANSPARENT);
        stage.setScene(scene);
        stage.show();
        stage.setX(0);
        stage.setY(0);

        background.setImage(copyBackground(stage));
        
        background.viewportProperty().bind(Bindings.createObjectBinding(() -> 
            new Rectangle2D(stage.getX(), stage.getY(), stage.getWidth(), stage.getHeight()), 
            stage.xProperty(), stage.yProperty(), stage.widthProperty(), stage.heightProperty()));
        
        background.setEffect(myBlur);

        makeDraggable(stage);
    }

    private static class Delta {
        double x,y;
    }

    public void makeDraggable(final Stage stage) {
        final Delta dragDelta = new Delta();
        btn.setOnMousePressed(mouseEvent -> {
            dragDelta.x = stage.getX() - mouseEvent.getScreenX();
            dragDelta.y = stage.getY() - mouseEvent.getScreenY();    
        });

        btn.setOnMouseDragged(mouseEvent -> {
            stage.setX(mouseEvent.getScreenX() + dragDelta.x);
            stage.setY(mouseEvent.getScreenY() + dragDelta.y);
            btn.setOpacity(0.0);
//            background.setImage(copyBackground(stage));
        });

         btn.setOnMouseReleased(mouseEvent -> {
            btn.setOpacity(1.0);
        });
    }

    private Image copyBackground(Stage stage) {
        
        Rectangle2D screen = Screen.getPrimary().getBounds() ;
        
        final int X = (int) screen.getMinX();
        final int Y = (int) screen.getMinX();
        final int W = (int) screen.getWidth();
        final int H = (int) screen.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!");
            return null;
        }
    }

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

One disadvantage here is that the image won't be representative of any changes to the desktop that happen independently from your application. One possible fix to this would be to use a WritableImage and to dynamically update portions of that image not covered by your window as the window changes position. The logic here would be a little tricky, but not too bad.

James_D
  • 201,275
  • 16
  • 291
  • 322