Created
December 27, 2011 10:49
-
-
Save skrb/1523258 to your computer and use it in GitHub Desktop.
FXEyes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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