I want a stack pane to simulate a FullHD display, that is 1920x1080.
For that I am using 640x360 which follows the same proportion and then I scale the image nodes inside the stackPane (which is called "screen") using:
image.getWidth()/(fullWidth/screen.getWidth())),
image.getHeight()/(fullHeight/screen.getHeight()))
This is working great only problem is I can't set the size for the "screen" therefore it keeps big black bars on the bottom and on the top of it.
As you can see in the image below the "screen" has a white border around it for making the black bars easier to notice.
screen snapshot
Here is the code that creates the stack pane and handles it:
DisplayPane constructor method
public DisplayPane(TemporalViewPane temporalViewPane, SteveMenuBar steveMenuBar, SpatialTemporalView spatialTemporalView){
setId("display-pane");
screen = new StackPane();
screen.setId("screen-pane");
controlButtonPane = new ControlButtonPane(screen, temporalViewPane, steveMenuBar, spatialTemporalView);
setCenter(screen);
setBottom(controlButtonPane);
}
ControlButtonPane constructor class method:
public ControlButtonPane(StackPane screen, TemporalViewPane temporalViewPane,SteveMenuBar steveMenuBar, SpatialTemporalView spatialTemporalView){
fullHDLabel = new Label("FullHD");
fullHDLabel.setPadding(new Insets(5,5,5,5));
screen.getChildren().add(fullHDLabel);
setId("control-button-pane");
this.steveMenuBar = steveMenuBar;
this.screen = screen;
this.screen.setAlignment(Pos.TOP_LEFT);
this.temporalViewPane = temporalViewPane;
this.spatialTemporalView = spatialTemporalView;
this.webView = new WebView();
createButtons();
setLeft(fullButtonPane);
setLeft(fullHDLabel);
setCenter(centerButtonPane);
setRight(refreshButtonPane);
createListeners();
createButtonActions();
}
CreateButtons method:
public void createButtons(){
run = new Button();
run.setDisable(true);
run.setId("run-button");
run.setTooltip(new Tooltip(Language.translate("run")));
play = new Button();
play.setDisable(true);
play.setId("play-button");
play.setTooltip(new Tooltip(Language.translate("play")));
pause = new Button();
pause.setDisable(true);
pause.setId("pause-button");
pause.setTooltip(new Tooltip(Language.translate("pause")));
stop = new Button();
stop.setDisable(true);
stop.setId("stop-button");
stop.setTooltip(new Tooltip(Language.translate("stop")));
centerButtonPane = new HBox();
centerButtonPane.setId("center-button-pane");
centerButtonPane.getChildren().add(run);
centerButtonPane.getChildren().add(play);
centerButtonPane.getChildren().add(pause);
centerButtonPane.getChildren().add(stop);
}
CreateListeners method:
private void createListeners(){
screen.widthProperty().addListener((obs, oldVal, newVal) -> {
if(newVal != oldVal){
screen.minHeightProperty().bind(screen.widthProperty().multiply(0.565));
screen.maxHeightProperty().bind(screen.widthProperty().multiply(0.565));
System.out.println("NEW WIDTH OF SCREEN IS: "+newVal);
}
});
screen.heightProperty().addListener((obs, oldVal, newVal) -> {
if(newVal != oldVal){
screen.minHeightProperty().bind(screen.widthProperty().multiply(0.565));
screen.maxHeightProperty().bind(screen.widthProperty().multiply(0.565));
System.out.println("NEW HEIGHT OF SCREEN IS: "+newVal);
}
});
And below is what I want to achieve. Currently, I have to run the application and then drag the side of the stack pane for resizing it as it should be.
how it should be snapshot
I've tried all set min/max/pref height/width and still haven't manage to achieve my goal. I don't know what else to do.
Any help would be great.
Problem was I was binding height to width when it should be the other way around since width can be resized much larger without wasting screen space.
So I changed the binding to:
private void createListeners(){
screen.widthProperty().addListener((obs, oldVal, newVal) -> {
if(newVal != oldVal){
screen.minWidthProperty().bind(screen.heightProperty().multiply(1.77778));
screen.maxWidthProperty().bind(screen.heightProperty().multiply(1.77778));
System.out.println("NEW WIDTH OF SCREEN IS: "+newVal);
}
});
screen.heightProperty().addListener((obs, oldVal, newVal) -> {
if(newVal != oldVal){
screen.minWidthProperty().bind(screen.heightProperty().multiply(1.77778));
screen.maxWidthProperty().bind(screen.heightProperty().multiply(1.77778));
System.out.println("NEW HEIGHT OF SCREEN IS: "+newVal);
}
});
}
Related
The current implementation of the VirtualFlow only makes scrollbars visible when view rect becomes less than control size. By control I mean ListView, TreeView and whatever standard virtualized controls. The problem is that vertical scrollbar appearance causes recalculation of the control width, namely it slightly shifts cell content to the left side. This is clearly noticeable and very uncomfortable movement.
I need to reserve some space for the vertical scrollbar beforehand, but none of controls provide API to manipulate VirtualFlow scrollbars behavior, which is very unfortunate API design. Not to mention that most of the implementations place scrollbars on top of the component, thus just overlapping the small part of it.
The question is, "Which is the best way to achieve this?". Paddings won't help, and JavaFX has no margins support. I could put control (e.g ListView) inside of ScrollPane, but I'd bet VirtualFlow won't continue to reuse cells in that case, so it's not a solution.
EXAMPLE:
Expand and collapse node2, it shifts lbRight content.
public class Launcher extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TreeItem<UUID> root = new TreeItem<>(UUID.randomUUID());
TreeView<UUID> tree = new TreeView<>(root);
tree.setCellFactory(list -> new CustomCell());
TreeItem<UUID> node0 = new TreeItem<>(UUID.randomUUID());
TreeItem<UUID> node1 = new TreeItem<>(UUID.randomUUID());
TreeItem<UUID> node2 = new TreeItem<>(UUID.randomUUID());
IntStream.range(0, 100)
.mapToObj(index -> new TreeItem<>(UUID.randomUUID()))
.forEach(node2.getChildren()::add);
root.getChildren().setAll(node0, node1, node2);
root.setExpanded(true);
node2.setExpanded(true);
BorderPane pane = new BorderPane();
pane.setCenter(tree);
Scene scene = new Scene(pane, 600, 600);
primaryStage.setTitle("Demo");
primaryStage.setScene(scene);
primaryStage.setOnCloseRequest(t -> Platform.exit());
primaryStage.show();
}
static class CustomCell extends TreeCell<UUID> {
public HBox hBox;
public Label lbLeft;
public Label lbRight;
public CustomCell() {
hBox = new HBox();
lbLeft = new Label();
lbRight = new Label();
lbRight.setStyle("-fx-padding: 0 20 0 0");
Region spacer = new Region();
HBox.setHgrow(spacer, Priority.ALWAYS);
hBox.getChildren().setAll(lbLeft, spacer, lbRight);
}
#Override
protected void updateItem(UUID uuid, boolean empty) {
super.updateItem(uuid, empty);
if (empty) {
setGraphic(null);
return;
}
String s = uuid.toString();
lbLeft.setText(s.substring(0, 6));
lbRight.setText(s.substring(6, 12));
setGraphic(hBox);
}
}
}
Reacting to
you can't just extend the VirtualFlow and override a method
certainly true if the method is deeply hidden by package/-private access (but even then: javafx is open source, checkout-edit-compile-distribute is also an option :). In this case we might get along with overriding public api as outlined below (not formally tested!).
VirtualFlow is the "layout" of cells and scrollBars: in particular, it has to cope with handling sizing/locating of all content w/out scrollBars being visible. There are options on how that can be done:
adjust cell width to always fill the viewport, increasing/decreasing when vertical scrollBar is hidden/visible
keep cell width constant such that there is always space left for the scrollBar, be it visible or not
keep cell width constant such that there is never space left the scrollBar, laying it out on top of cell
others ??
Default VirtualFlow implements the first with no option to switch to any other. (might be candidate for an RFE, feel free to report :).
Digging into the code reveals that the final sizing of the cells is done by calling cell.resize(..) (as already noted and exploited in the self-answer) near the end of the layout code. Overriding a custom cell's resize is perfectly valid and a good option .. but not the only one, IMO. An alternative is to
extend VirtualFlow and override layoutChildren to adjust cell width as needed
extend TreeViewSkin to use the custom flow
Example code (requires fx12++):
public static class XVirtualFlow<I extends IndexedCell> extends VirtualFlow<I> {
#Override
protected void layoutChildren() {
super.layoutChildren();
fitCellWidths();
}
/**
* Resizes cell width to accomodate for invisible vbar.
*/
private void fitCellWidths() {
if (!isVertical() || getVbar().isVisible()) return;
double width = getWidth() - getVbar().getWidth();
for (I cell : getCells()) {
cell.resize(width, cell.getHeight());
}
}
}
public static class XTreeViewSkin<T> extends TreeViewSkin<T>{
public XTreeViewSkin(TreeView<T> control) {
super(control);
}
#Override
protected VirtualFlow<TreeCell<T>> createVirtualFlow() {
return new XVirtualFlow<>();
}
}
On-the-fly usage:
TreeView<UUID> tree = new TreeView<>(root) {
#Override
protected Skin<?> createDefaultSkin() {
return new XTreeViewSkin<>(this);
}
};
Ok, this is summary based on #kleopatra comments and OpenJFX code exploration. There will be no code to solve the problem, but still maybe it will spare some time to someone.
As being said, it's VirtualFlow responsibility to manage virtualized control viewport size. All magic happens in the layoutChildren(). First it computes scrollbars visibility and then recalculates size of all children based on that knowledge. Here is the code which causes the problem.
Since all implementation details are private or package-private, you can't just extend the VirtualFlow and override method or two, you have to copy-paste and edit entire class (to remove one line, yes). Given that, changing internal components layout could be a better option.
Sometimes, I adore languages those have no encapsulation.
UPDATE:
I've solved the problem. There is no way no reserve space for vertical scrollbar without tweaking JavaFX internals, but we can limit cell width, so it would be always less than TreeView (or List View) width. Here is simple example.
public class Launcher extends Application {
public static final double SCENE_WIDTH = 500;
#Override
public void start(Stage primaryStage) {
TreeItem<UUID> root = new TreeItem<>(UUID.randomUUID());
TreeView<UUID> tree = new TreeView<>(root);
tree.setCellFactory(list -> new CustomCell(SCENE_WIDTH));
TreeItem<UUID> node0 = new TreeItem<>(UUID.randomUUID());
TreeItem<UUID> node1 = new TreeItem<>(UUID.randomUUID());
TreeItem<UUID> node2 = new TreeItem<>(UUID.randomUUID());
IntStream.range(0, 100)
.mapToObj(index -> new TreeItem<>(UUID.randomUUID()))
.forEach(node2.getChildren()::add);
root.getChildren().setAll(node0, node1, node2);
root.setExpanded(true);
node2.setExpanded(true);
BorderPane pane = new BorderPane();
pane.setCenter(tree);
Scene scene = new Scene(pane, SCENE_WIDTH, 600);
primaryStage.setTitle("Demo");
primaryStage.setScene(scene);
primaryStage.setOnCloseRequest(t -> Platform.exit());
primaryStage.show();
}
static class CustomCell extends TreeCell<UUID> {
public static final double RIGHT_PADDING = 40;
/*
this value depends on tree disclosure node width
in my case it's enforced via CSS, so I always know exact
value of this padding
*/
public static final double INDENT_PADDING = 14;
public HBox hBox;
public Label lbLeft;
public Label lbRight;
public double maxWidth;
public CustomCell(double maxWidth) {
this.maxWidth = maxWidth;
hBox = new HBox();
lbLeft = new Label();
lbRight = new Label();
lbRight.setPadding(new Insets(0, RIGHT_PADDING, 0, 0));
Region spacer = new Region();
HBox.setHgrow(spacer, Priority.ALWAYS);
hBox.getChildren().setAll(lbLeft, spacer, lbRight);
}
#Override
protected void updateItem(UUID uuid, boolean empty) {
super.updateItem(uuid, empty);
if (empty) {
setGraphic(null);
return;
}
String s = uuid.toString();
lbLeft.setText(s.substring(0, 6));
lbRight.setText(s.substring(6, 12));
setGraphic(hBox);
}
#Override
public void resize(double width, double height) {
// enforce item width
double maxCellWidth = getTreeView().getWidth() - RIGHT_PADDING;
double startLevel = getTreeView().isShowRoot() ? 0 : 1;
double itemLevel = getTreeView().getTreeItemLevel(getTreeItem());
if (itemLevel > startLevel) {
maxCellWidth = maxCellWidth - ((itemLevel - startLevel) * INDENT_PADDING);
}
hBox.setPrefWidth(maxCellWidth);
hBox.setMaxWidth(maxCellWidth);
super.resize(width, height);
}
}
}
It's far from perfect, but it works.
I trying to build map which consist of regions(states) and when mouse entered to some region, I need handle it. Have many png images which represent each region separately. I blend my images and got what I want, but I can't handle some region.
For instance:
It's a first region img
This is a second region img
As result I got:
Code:
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("view/MapView.fxml"));
Pane root = loader.load();
primaryStage.setTitle("Map");
primaryStage.setScene(new Scene(root, 700, 700));
primaryStage.show();
//First region
File file = new File("src/res/img/region1.png");
Canvas canvas = new Canvas(700, 700);
canvas.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, event -> System.out.println("Region 1"));
GraphicsContext graphicsContext = canvas.getGraphicsContext2D();
graphicsContext.drawImage(new Image(file.toURI().toString()), 0, 0);
root.getChildren().add(canvas);
//Second region
file = new File("src/res/img/region2.png");
canvas = new Canvas(700, 700);
canvas.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, event -> System.out.println("Region 2"));
graphicsContext = canvas.getGraphicsContext2D();
graphicsContext.drawImage(new Image(file.toURI().toString()), 0, 0);
root.getChildren().add(canvas);
}
In console I got always "Region 2".
Please give me tips for research. Thanks in advance!
You can use an ImageView and setPickOnBounds for that.
Example code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class LayersWithMouseEvents extends Application {
#Override
public void start(Stage primaryStage) {
// root
StackPane root = new StackPane();
// create layers
Pane region1Layer = new Pane();
Pane region2Layer = new Pane();
// add layers
root.getChildren().addAll(region1Layer, region2Layer);
// load images
ImageView region1ImageView = new ImageView( new Image( getClass().getResource("region1.png").toExternalForm()));
ImageView region2ImageView = new ImageView( new Image( getClass().getResource("region2.png").toExternalForm()));
// add images
region1Layer.getChildren().add(region1ImageView);
region2Layer.getChildren().add(region2ImageView);
// mouse handler
region1Layer.setOnMousePressed(e -> System.out.println("Region 1: " + e));
region2Layer.setOnMousePressed(e -> System.out.println("Region 2: " + e));
// this is the magic that allows you to click on the separate layers, but ONLY(!) as long as the layer is transparent
region1Layer.setPickOnBounds(false);
region2Layer.setPickOnBounds(false);
primaryStage.setScene(new Scene(root, 800, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
In the future please create an MCVE with focus on complete. Nobody wants to waste their time in order to make your incomplete example work.
To my opinion using a canvas here is not the right approach. If you would define your regions as polygons and add them to the scene graph you can attach a listener to each of these polygons and then react accordingly if the mouse is over some region. Maybe this is also possible with image views but I have never tried wether a transparent region of an image is also mouse transparent which seems to be necessary in that case. For a program of mine I used polygons and it works nicely.
The image data can be used to determine the color of the pixels under the mouse cursor. If the pixel is not fully transparent, then the cursor is on that region.
To retrieve this information you need to use a listener for the mouse moved event. For simplicity you can use a property with a listener attached to trigger the region enter/leave events:
The following example assumes you keep references to the images used for the regions named image1 and image2:
PixelReader reader1 = image1.getPixelReader();
PixelReader reader2 = image2.getPixelReader();
SimpleIntegerProperty region = new SimpleIntegerProperty(-1);
region.addListener((observable, oldValue, newValue) -> {
if (newValue.intValue() < 0) {
System.out.println("region left");
} else {
System.out.println("Region " + (newValue.intValue() + 1));
}
});
canvas.setOnMouseMoved(event -> {
int x = (int) event.getX();
int y = (int) event.getY();
if (x < image1.getWidth() && y < image1.getHeight() && reader1.getColor(x, y).getOpacity() != 0) {
region.set(0);
} else if (x < image2.getWidth() && y < image2.getHeight() && reader2.getColor(x, y).getOpacity() != 0) {
region.set(1);
} else {
region.set(-1);
}
});
Also there is no need to create multiple Canvas to paint the images.
this is my first game using JavaFX so I admittedly have made some bad design decisions, probably.
Anyway, I want to transition from a splash page (Splash class) to a cutscene (Cutscene class) and then to a playable level (PlayableLevel class). The game is launched from my Main class and transitions are supposed to be done with keyboard inputs (Enter button).
The method in Main that starts the game looks like this:
public void start (Stage s) {
// create your own game here
Splash splashPage = new Splash();
Cutscene cs = new Cutscene();
PlayableLevel play = new PlayableLevel();
// attach game to the stage and display it
Scene scene0 = splashPage.init(SIZE, SIZE);
Scene scene1 = cs.init(SIZE, SIZE, 0);
Scene scene2 = play.init(SIZE, SIZE, 0);
s.setScene(scene0);
s.show();
// sets the game's loop
KeyFrame frame = new KeyFrame(Duration.millis(MILLISECOND_DELAY),
e -> myGame.step(SECOND_DELAY));
Timeline animation = new Timeline();
animation.setCycleCount(Timeline.INDEFINITE);
animation.getKeyFrames().add(frame);
animation.play();
}
My question in particular is what should I do to make it so that the Splash class can communicate with the Main class so that once a keystroke event is recorded, the stage can set a new scene? I'm currently reading about EventHandlers but I'm unsure of the exact implementation as of now.
EDIT: One idea I had was to make a linked list of Scenes, and then once some event happens (keystroke), then I would set the scene to the next one in the list.
You could do something like this:
public class Splash {
private Runnable nextSceneHandler ;
public void setNextSceneHandler(Runnable handler) {
nextSceneHandler = handler ;
}
public Scene init(double width, double height) {
Scene scene = new Scene();
// Just an example handler, you could do the same for
// button events, menus, etc., or even just handlers for the
// end of an animation
scene.setOnKeyPressed(e -> {
if (nextSceneHandler != null) {
if (e.getCode() == ...) {
nextSceneHandler.run();
}
}
}
// existing code...
}
// existing code ...
}
and similarly for CutScene.
Then
public void start (Stage s) {
// create your own game here
Splash splashPage = new Splash();
Cutscene cs = new Cutscene();
PlayableLevel play = new PlayableLevel();
// attach game to the stage and display it
Scene scene0 = splashPage.init(SIZE, SIZE);
Scene scene1 = cs.init(SIZE, SIZE, 0);
Scene scene2 = play.init(SIZE, SIZE, 0);
splashPage.setNextSceneHandler(() -> s.setScene(scene1));
cs.setNextSceneHandler(() -> s.setScene(scene2));
s.setScene(scene0);
s.show();
// sets the game's loop
KeyFrame frame = new KeyFrame(Duration.millis(MILLISECOND_DELAY),
e -> myGame.step(SECOND_DELAY));
Timeline animation = new Timeline();
animation.setCycleCount(Timeline.INDEFINITE);
animation.getKeyFrames().add(frame);
animation.play();
}
Your linked list idea should work too. You need a mechanism for passing the linked list instance (or perhaps an iterator from it) to each of the scene generating classes; their event handlers would execute code like
scene.getWindow().setScene(sceneIterator.next());
I kind of prefer setting the runnable on the objects, as it feels a little more flexible. Just a question of style though.
I can trivially create a scroll pane in JavaFX that only scrolls horizontally like so:
ScrollPane scroll = new ScrollPane(lcp);
scroll.setPannable(true);
scroll.setFitToHeight(true);
scroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
However, the mouse scroll wheel still tries to scroll vertically rather than horizontally in this case (unless I specifically scroll over the horizontal scroll bar.)
How can I set up the scroll pane so that the mouse wheel pans horizontally?
Here is the example application that I wrote for you and does exactly what you want:
public class Test extends Application {
ScrollPane scrollPane;
int pos = 0;
final int minPos = 0;
final int maxPos = 100;
#Override
public void start(Stage primaryStage) {
Label label = new Label("TEXT!!!!!!!TEXT!!!!!!!TEXT!!!!!!!TEXT!!!!!!!TEXT!!!!!!!TEXT");
label.setPrefSize(500, 100);
label.setOnScroll(new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
if (event.getDeltaY() > 0)
scrollPane.setHvalue(pos == minPos ? minPos : pos--);
else
scrollPane.setHvalue(pos == maxPos ? maxPos : pos++);
}
});
scrollPane = new ScrollPane();
scrollPane.setHmin(minPos);
scrollPane.setHmax(maxPos);
scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);
scrollPane.setHbarPolicy(ScrollBarPolicy.ALWAYS);
scrollPane.setPannable(true);
scrollPane.setFitToHeight(true);
scrollPane.setContent(label);
BorderPane root = new BorderPane();
root.setPrefSize(200, 100);
root.setCenter(scrollPane);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have also been looking for a solution and found this one from Ugurcan Yildirim but didn't like the fact that the natural scroll bar length and speed is modified also. This one worked for me:
scrollPane.setOnScroll(event -> {
if(event.getDeltaX() == 0 && event.getDeltaY() != 0) {
scrollPane.setHvalue(scrollPane.getHvalue() - event.getDeltaY() / this.allComments.getWidth());
}
});
event.getDeltaX() == 0 just to be sure that the user is only using the mouse wheel and nothing is adding up
this.allComments is the content of the scrollPane (a HBox in my case). By dividing the delta y value by it's content width the scroll speed is natural according to the amount of content to scroll.
Let's say I've got a StackPane which has a BackgroundImage as background and another StackPane (or another component, if neccessary) as a child. The child covers only a part of the parent StackPane.
I'd like to know how to apply a GaussianBlur just to the area the child covers, so that the BackgroundImageis blurry in this area.
The size of the child changes when the parent is resized. It would be perfect to get a solution that will resize just in time, too.
If you want to do it manually, you can use the snapshot function to create a snapshot image, blur it and apply it to the child every time the parent is resized.
However, invoking snapshot all the time will cause performance loss. I rather suggest you create 2 images, one normal and one blurred, and display a viewport of the blurred one.
Here's a more "complex" example with a circle where the viewport isn't sufficient. The clip method is used in this case:
public class Lens extends Application {
Image image = new Image( "http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/800px-Siberischer_tiger_de_edit02.jpg");
CirclePane circlePane;
#Override
public void start(Stage primaryStage) {
ImageView normalImageView = new ImageView( image);
ImageView blurredImageView = new ImageView( image);
blurredImageView.setEffect(new GaussianBlur( 40));
Group root = new Group();
root.getChildren().addAll( normalImageView);
Scene scene = new Scene( root, 1024, 768);
primaryStage.setScene( scene);
primaryStage.show();
// pane with clipped area
circlePane = new CirclePane( blurredImageView);
makeDraggable( circlePane);
root.getChildren().addAll( circlePane);
}
public static void main(String[] args) {
launch(args);
}
private class CirclePane extends Pane {
ImageView blurredImageView;
ImageView clippedView = new ImageView();
public CirclePane( ImageView blurredImageView) {
this.blurredImageView = blurredImageView;
// new imageview
update();
getChildren().addAll( clippedView);
}
public void update() {
// create circle
Circle circle = new Circle( 200);
circle.relocate( getLayoutX(), getLayoutY());
// clip image by circle
blurredImageView.setClip(circle);
// non-clip area should be transparent
SnapshotParameters parameters = new SnapshotParameters();
parameters.setFill(Color.TRANSPARENT);
// new image from clipped image
WritableImage wim = null;
wim = blurredImageView.snapshot(parameters, wim);
clippedView.setImage( wim);
}
}
// make node draggable
class DragContext {
double x;
double y;
}
public void makeDraggable( Node node) {
final DragContext dragDelta = new DragContext();
node.setOnMousePressed(mouseEvent -> {
dragDelta.x = node.getBoundsInParent().getMinX() - mouseEvent.getScreenX();
dragDelta.y = node.getBoundsInParent().getMinY() - mouseEvent.getScreenY();
});
node.setOnMouseDragged(mouseEvent -> {
node.relocate( mouseEvent.getScreenX() + dragDelta.x, mouseEvent.getScreenY() + dragDelta.y);
circlePane.update();
});
}
}
Just click on the circle and drag it around.