package greenfoot.guifx;

import bluej.utility.javafx.FXPlatformConsumer;
import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;
import threadchecker.OnThread;
import threadchecker.Tag;


| A class for handling the world part of the main GreenfootStage window. | @OnThread(value = Tag.FXPlatform, ignoreParent = true) public class WorldDisplay extends StackPane{ private final ImageView imageView = new ImageView(); private final AskPaneFX askPane = new AskPaneFX(); private final Rectangle actorHighlight = new Rectangle(); private final Animation actorHighlightAnimation; public WorldDisplay() { Pane highlightPane = new Pane(actorHighlight); highlightPane.setMouseTransparent(true); StackPane stackPane = new StackPane(imageView, askPane, highlightPane); stackPane.getStyleClass().add("world-display-wrapper"); stackPane.setMaxWidth(USE_PREF_SIZE); stackPane.setMaxHeight(USE_PREF_SIZE); getChildren().addAll(stackPane); setMinWidth(200); setMinHeight(200); actorHighlight.setVisible(false); actorHighlight.getStyleClass().add("actor-highlight"); actorHighlight.getStrokeDashArray().setAll(6.0, 10.0); actorHighlightAnimation = new Timeline( new KeyFrame(Duration.ZERO, new KeyValue(actorHighlight.strokeDashOffsetProperty(), 0), new KeyValue(actorHighlight.strokeProperty(), Color.BLACK)), new KeyFrame(Duration.seconds(2), new KeyValue(actorHighlight.strokeProperty(), Color.WHITE, Interpolator.EASE_BOTH)), new KeyFrame(Duration.seconds(4), new KeyValue(actorHighlight.strokeDashOffsetProperty(), 16, Interpolator.LINEAR), new KeyValue(actorHighlight.strokeProperty(), Color.BLACK, Interpolator.EASE_BOTH))); actorHighlightAnimation.setCycleCount(Animation.INDEFINITE); }
| Sets the world image. Turns off any greying effect. | | Returns true if the world changed size | public boolean setImage(Image image) { Image oldImage = imageView.getImage(); boolean newSize = oldImage == null || image == null || image.getWidth() != oldImage.getWidth() || image.getHeight() != oldImage.getHeight(); imageView.setImage(image); imageView.setEffect(null); return newSize; }
| Greys out the world to indicate it isn't in a valid state. | Turned off by next setImage call. | public void greyOutWorld() { ColorAdjust grey = new ColorAdjust(0.0, -1.0, -0.1, 0.0); GaussianBlur blur = new GaussianBlur(); blur.setInput(grey); imageView.setEffect(blur); }
| Show the ask panel with the given prompt, and call the given callback once | the user gives an answer. It is safe to call this method repeatedly for the | same ask request without disturbing the GUI. | @OnThread(Tag.FXPlatform) public void ensureAsking(String prompt, FXPlatformConsumer<String> withAnswer) { imageView.setDisable(true); greyOutWorld(); askPane.setVisible(true); askPane.setPrompt(prompt); askPane.focusTextEntry(); askPane.setWithAnswer(ans -> { askPane.setVisible(false); imageView.setDisable(false); imageView.setEffect(null); withAnswer.accept(ans); requestFocus(); }); }
| Is the ask pane currently showing? | public boolean isAsking() { return askPane.isVisible(); }
| Converts a point in the scene to world coordinates. | public Point2D sceneToWorld(Point2D point2D) { return imageView.sceneToLocal(point2D); }
| Converts a point in world coordinates into screen coordinates. | public Point2D worldToScreen(Point2D point2D) { return imageView.localToScreen(point2D); }
| Checks if the given point (in world coordinates) lies inside the world or not. | public boolean worldContains(Point2D point2D) { return imageView.contains(point2D); }
| Is the world greyed out? (It will be if there has been a call | to greyOutWorld(), but not yet followed by setImage) | public boolean isGreyedOut() { return imageView.getEffect() != null; }
| Returns a rendered image which illustrates a snapshot of the world display. | @return the rendered image | public Image getSnapshot() { return imageView.snapshot(null, null); }
| Get the node containing the actual world image (sans any spacing/border). | public Node getImageView() { return imageView; }
| Highlight the given actor bounds with a box overlay. | @param x The centre X coordinate (in pixels, in the world) | @param y The centre Y coordinate (in pixels, in the world) | @param width The width of the image (in pixels) | @param height The height of the image (in pixels) | @param rotation The rotation of the actor (in degrees) | public void setActorHighlight(int x, int y, int width, int height, int rotation) { if (x < -width || x > imageView.getImage().getWidth() + width || y < -height || y > imageView.getImage().getHeight() + height || width <= 0 || width > imageView.getImage().getWidth() || height <=0 || height > imageView.getImage().getHeight()) { return; } actorHighlight.setX(x - width / 2.0); actorHighlight.setY(y - height / 2.0); actorHighlight.setWidth(width); actorHighlight.setHeight(height); actorHighlight.getTransforms().setAll(new Rotate(rotation, x, y)); actorHighlight.setVisible(true); actorHighlightAnimation.playFromStart(); }
| Hide the highlight set by setActorHighlight | public void clearActorHighlight() { actorHighlightAnimation.stop(); actorHighlight.setVisible(false); } }
top, use, map, class WorldDisplay

.   WorldDisplay
.   setImage
.   greyOutWorld
.   ensureAsking
.   isAsking
.   sceneToWorld
.   worldToScreen
.   worldContains
.   isGreyedOut
.   getSnapshot
.   getImageView
.   setActorHighlight
.   clearActorHighlight




222 neLoCode + 24 LoComm