FXEyes
package net.javainthebox.fxeyes; | |
import javafx.beans.binding.DoubleBinding; | |
import javafx.beans.property.DoubleProperty; | |
import javafx.beans.property.SimpleDoubleProperty; | |
import javafx.geometry.Point2D; | |
import javafx.scene.Parent; | |
import javafx.scene.paint.Color; | |
import javafx.scene.shape.Circle; | |
import javafx.scene.shape.Ellipse; | |
public class Eyes extends Parent { | |
private static final double MINIMAM_STROKE = 1.0; | |
private static final double SIZE_RATIO = 10.0; | |
// Eye Position | |
private DoubleProperty locationX = new SimpleDoubleProperty(); | |
private DoubleProperty locationY = new SimpleDoubleProperty(); | |
private DoubleProperty width = new SimpleDoubleProperty(); | |
private DoubleProperty height = new SimpleDoubleProperty(); | |
// Left Back Eye Position | |
private DoubleProperty leftX = new SimpleDoubleProperty(); | |
private DoubleProperty leftY = new SimpleDoubleProperty(); | |
// Right Back Eye Position | |
private DoubleProperty rightX = new SimpleDoubleProperty(); | |
private DoubleProperty rightY = new SimpleDoubleProperty(); | |
// Eye Stroke Width is determined by scene size. | |
private DoubleBinding strokeWidth = new DoubleBinding() { | |
{ | |
super.bind(width, height); | |
} | |
@Override | |
protected double computeValue() { | |
// Strok Width is dicided by screen size. | |
double stroke; | |
if (width.get() < height.get()) { | |
stroke = width.get() / SIZE_RATIO; | |
} else { | |
stroke = height.get() / SIZE_RATIO; | |
} | |
return (stroke < MINIMAM_STROKE) ? MINIMAM_STROKE : stroke; | |
} | |
}; | |
public Eyes() { | |
createEyes(); | |
} | |
public DoubleProperty locationXProperty() { | |
return locationX; | |
} | |
public DoubleProperty locationYProperty() { | |
return locationY; | |
} | |
public DoubleProperty widthProperty() { | |
return width; | |
} | |
public DoubleProperty heightProperty() { | |
return height; | |
} | |
public void setMouseLocation(double mouseX, double mouseY) { | |
double x = mouseX - locationX.get(); | |
double y = -mouseY + locationY.get() + height.get(); | |
double cx = width.get() / 4; | |
double cy = height.get() / 2; | |
// Update Left Black Eye Position | |
Point2D left = calculateEyePosition(x, y, cx, cy); | |
leftX.set(left.getX()); | |
leftY.set(left.getY()); | |
// Update Right Black Eye Position | |
cx = width.get() / 4 * 3; | |
Point2D right = calculateEyePosition(x, y, cx, cy); | |
rightX.set(right.getX()); | |
rightY.set(right.getY()); | |
} | |
private Point2D calculateEyePosition(double x, double y, double cx, double cy) { | |
double theta = Math.atan2(y - cy, x - cx); | |
double hr = width.get() / 4 - 2 * strokeWidth.get(); | |
double eyeX; | |
double eyeY; | |
if (Math.abs(x - cx) > Math.abs(hr * Math.cos(theta))) { | |
eyeX = cx + hr * Math.cos(theta); | |
} else { | |
eyeX = x; | |
} | |
double vr = height.get() / 2 - 2 * strokeWidth.get(); | |
if (Math.abs(y - cy) > Math.abs(vr * Math.sin(theta))) { | |
eyeY = height.get() - cy - vr * Math.sin(theta); | |
} else { | |
eyeY = height.get() - y; | |
} | |
return new Point2D(eyeX, eyeY); | |
} | |
private void createEyes() { | |
// Left Eye | |
Ellipse left = new Ellipse(); | |
left.centerXProperty().bind(width.divide(4.0)); | |
left.centerYProperty().bind(height.divide(2.0)); | |
left.radiusXProperty().bind(width.divide(4.0).subtract(strokeWidth.divide(2.0))); | |
left.radiusYProperty().bind(height.divide(2.0).subtract(strokeWidth.divide(2.0))); | |
left.setStroke(Color.BLACK); | |
left.strokeWidthProperty().bind(strokeWidth); | |
left.setFill(Color.WHITE); | |
getChildren().add(left); | |
// Left Black Eye | |
Circle leftBlackEye = new Circle(); | |
leftBlackEye.centerXProperty().bind(leftX); | |
leftBlackEye.centerYProperty().bind(leftY); | |
leftBlackEye.radiusProperty().bind(strokeWidth); | |
leftBlackEye.setFill(Color.BLACK); | |
getChildren().add(leftBlackEye); | |
// Right Eye | |
Ellipse right = new Ellipse(); | |
right.centerXProperty().bind(width.divide(4.0).multiply(3.0)); | |
right.centerYProperty().bind(height.divide(2.0)); | |
right.radiusXProperty().bind(width.divide(4.0).subtract(strokeWidth.divide(2.0))); | |
right.radiusYProperty().bind(height.divide(2.0).subtract(strokeWidth.divide(2.0))); | |
right.setStroke(Color.BLACK); | |
right.strokeWidthProperty().bind(strokeWidth); | |
right.setFill(Color.WHITE); | |
getChildren().add(right); | |
// Left Black Eye | |
Circle rightBlackEye = new Circle(); | |
rightBlackEye.centerXProperty().bind(rightX); | |
rightBlackEye.centerYProperty().bind(rightY); | |
rightBlackEye.radiusProperty().bind(strokeWidth); | |
rightBlackEye.setFill(Color.BLACK); | |
getChildren().add(rightBlackEye); | |
} | |
} |
package net.javainthebox.fxeyes; | |
import java.awt.MouseInfo; | |
import java.awt.PointerInfo; | |
import java.awt.event.ActionListener; | |
import javafx.application.Application; | |
import javafx.application.Platform; | |
import javafx.event.ActionEvent; | |
import javafx.event.EventHandler; | |
import javafx.scene.Group; | |
import javafx.scene.Scene; | |
import javafx.scene.control.ContextMenu; | |
import javafx.scene.control.Label; | |
import javafx.scene.control.MenuItem; | |
import javafx.scene.input.MouseButton; | |
import javafx.scene.input.MouseEvent; | |
import javafx.stage.Stage; | |
import javafx.stage.StageStyle; | |
import javafx.stage.WindowEvent; | |
import javax.swing.SwingUtilities; | |
import javax.swing.Timer; | |
public class FXEyes extends Application { | |
private final static double INIT_X = 200.0; | |
private final static double INIT_Y = 200.0; | |
private final static double INIT_WIDTH = 200.0; | |
private final static double INIT_HEIGHT = 200.0; | |
private final static String DECORATE = "Decorate"; | |
private final static String UNDECORATE = "Undecorate"; | |
private Stage stage; | |
private Scene scene; | |
private Eyes eyes; | |
private ContextMenu menu; | |
private MenuItem styleItem; | |
// for drag stage | |
private double dx; | |
private double dy; | |
private Timer timer; | |
@Override | |
public void start(Stage stage) { | |
this.stage = stage; | |
initMenu(); | |
initStage(StageStyle.TRANSPARENT, 200, 200, INIT_WIDTH, INIT_HEIGHT); | |
startSwingEDT(); | |
} | |
private void initStage(StageStyle style, double x, double y, double width, double height) { | |
stage.setTitle("FXEyes"); | |
stage.setX(x); | |
stage.setY(y); | |
stage.initStyle(style); | |
stage.setOnCloseRequest(new EventHandler<WindowEvent>() { | |
@Override | |
public void handle(WindowEvent event) { | |
terminateSwingEDT(); | |
} | |
}); | |
Group root = new Group(); | |
scene = new Scene(root, width, height); | |
scene.setFill(null); | |
root.getChildren().add(new Label("")); | |
eyes = new Eyes(); | |
eyes.widthProperty().bind(scene.widthProperty()); | |
eyes.heightProperty().bind(scene.heightProperty()); | |
eyes.locationXProperty().bind(stage.xProperty()); | |
eyes.locationYProperty().bind(stage.yProperty()); | |
eyes.setOnMouseClicked(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
if (event.getButton() == MouseButton.SECONDARY) { | |
menu.show(eyes, event.getScreenX(), event.getScreenY()); | |
} | |
} | |
}); | |
eyes.setOnMousePressed(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
dx = event.getSceneX() + scene.getX(); | |
dy = event.getSceneY() + scene.getY(); | |
} | |
}); | |
eyes.setOnMouseDragged(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
FXEyes.this.stage.setX(event.getScreenX() - dx); | |
FXEyes.this.stage.setY(event.getScreenY() - dy); | |
} | |
}); | |
root.getChildren().add(eyes); | |
stage.setScene(scene); | |
stage.show(); | |
} | |
private void initMenu() { | |
menu = new ContextMenu(); | |
styleItem = new MenuItem(DECORATE); | |
styleItem.setOnAction(new EventHandler<ActionEvent>() { | |
@Override | |
public void handle(ActionEvent event) { | |
// Creating new stage with same size and same position. | |
double x = stage.getX() + scene.getX(); | |
double y = stage.getY() + scene.getY(); | |
double width = scene.getWidth(); | |
double height = scene.getHeight(); | |
StageStyle style = stage.getStyle(); | |
stage.close(); | |
stage = new Stage(); | |
if (style == StageStyle.TRANSPARENT) { | |
initStage(StageStyle.DECORATED, x, y, width, height); | |
styleItem.setText(UNDECORATE); | |
} else { | |
initStage(StageStyle.TRANSPARENT, x, y, width, height); | |
styleItem.setText(DECORATE); | |
} | |
} | |
}); | |
menu.getItems().add(styleItem); | |
MenuItem exitItem = new MenuItem("Exit"); | |
exitItem.setOnAction(new EventHandler<ActionEvent>() { | |
@Override | |
public void handle(ActionEvent event) { | |
// Stop JavaFX EDT | |
Platform.exit(); | |
// Stop Swing EDT | |
terminateSwingEDT(); | |
} | |
}); | |
menu.getItems().add(exitItem); | |
} | |
private void startSwingEDT() { | |
SwingUtilities.invokeLater(new Runnable() { | |
@Override | |
public void run() { | |
timer = new Timer(50, new ActionListener() { | |
@Override | |
public void actionPerformed(java.awt.event.ActionEvent event) { | |
final PointerInfo info = MouseInfo.getPointerInfo(); | |
Platform.runLater(new Runnable() { | |
@Override | |
public void run() { | |
eyes.setMouseLocation(info.getLocation().getX() - scene.getX(), | |
info.getLocation().getY() - scene.getY()); | |
} | |
}); | |
} | |
}); | |
timer.start(); | |
} | |
}); | |
} | |
private void terminateSwingEDT() { | |
SwingUtilities.invokeLater(new Runnable() { | |
@Override | |
public void run() { | |
timer.stop(); | |
} | |
}); | |
} | |
public static void main(String[] args) { | |
Application.launch(args); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment