/*
* RadialMenuDemo
* Copyright 2012 (C) Mr LoNee - (Laurent NICOLAS) - www.mrlonee.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
package com.mrlonee.radialmenu;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
import javafx.animation.Animation;
import javafx.animation.FadeTransition;
import javafx.animation.FadeTransitionBuilder;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.ParallelTransition;
import javafx.animation.ParallelTransitionBuilder;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.CheckBoxBuilder;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.ColorPickerBuilder;
import javafx.scene.control.LabelBuilder;
import javafx.scene.control.Slider;
import javafx.scene.control.SliderBuilder;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.ImageViewBuilder;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineBuilder;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.RectangleBuilder;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Duration;
public class RadialMenuDemo extends Application {
protected ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
1);
protected Group container = new Group();
protected int counter;
protected RadialMenu radialMenu;
protected boolean show;
protected double lastOffsetValue;
protected double lastInitialAngleValue;
public static void main(final String[] args) {
RadialMenuDemo.launch(args);
}
private void hideRadialMenu() {
this.lastInitialAngleValue = this.radialMenu.getInitialAngle();
this.lastOffsetValue = this.radialMenu.getOffset();
final FadeTransition fade = FadeTransitionBuilder.create()
.node(this.radialMenu).fromValue(1).toValue(0)
.duration(Duration.millis(300))
.onFinished(new EventHandler<ActionEvent>() {
@Override
public void handle(final ActionEvent arg0) {
RadialMenuDemo.this.radialMenu.setVisible(false);
}
}).build();
// final Animation offset = new Timeline(new KeyFrame(Duration.ZERO,
// new KeyValue(this.radialMenu.getOffsetProperty(),
// this.lastOffsetValue)), new KeyFrame(
// Duration.millis(400), new KeyValue(
// this.radialMenu.getOffsetProperty(), 0)));
//
// final Animation angle = new Timeline(new KeyFrame(Duration.ZERO,
// new KeyValue(this.radialMenu.getInitialAngleProperty(),
// this.lastInitialAngleValue)), new KeyFrame(
// Duration.millis(400), new KeyValue(
// this.radialMenu.getInitialAngleProperty(),
// this.lastInitialAngleValue + 20)));
final ParallelTransition transition = ParallelTransitionBuilder
.create().children(fade).build();
transition.play();
}
private void showRadialMenu(final double x, final double y) {
if (this.radialMenu.isVisible()) {
this.lastInitialAngleValue = this.radialMenu.getInitialAngle();
this.lastOffsetValue = this.radialMenu.getOffset();
}
this.radialMenu.setLayoutX(x);
this.radialMenu.setLayoutY(y);
this.radialMenu.setVisible(true);
final FadeTransition fade = FadeTransitionBuilder.create()
.node(this.radialMenu).duration(Duration.millis(400))
.fromValue(0).toValue(1.0).build();
final Animation offset = new Timeline(new KeyFrame(Duration.ZERO,
new KeyValue(this.radialMenu.getOffsetProperty(), 0)),
new KeyFrame(Duration.millis(300), new KeyValue(this.radialMenu
.getOffsetProperty(), this.lastOffsetValue)));
final Animation angle = new Timeline(new KeyFrame(Duration.ZERO,
new KeyValue(this.radialMenu.getInitialAngleProperty(),
this.lastInitialAngleValue + 20)), new KeyFrame(
Duration.millis(300), new KeyValue(
this.radialMenu.getInitialAngleProperty(),
this.lastInitialAngleValue)));
final ParallelTransition transition = ParallelTransitionBuilder
.create().children(fade, offset, angle).build();
transition.play();
}
public Node[] createShapes() {
this.counter++;
this.radialMenu = new RadialMenu(-23, 30, 100, 10, Color.LIGHTBLUE,
Color.LIGHTBLUE.darker(), Color.DARKBLUE, false);
final ImageView play = ImageViewBuilder.create()
.image(new Image("file:images/play.png")).build();
final ImageView stop = ImageViewBuilder.create()
.image(new Image("file:images/stop.png")).build();
final ImageView pause = ImageViewBuilder.create()
.image(new Image("file:images/pause.png")).build();
final ImageView forward = ImageViewBuilder.create()
.image(new Image("file:images/forward.png")).build();
final ImageView backward = ImageViewBuilder.create()
.image(new Image("file:images/backward.png")).build();
final EventHandler<ActionEvent> handler = new EventHandler<ActionEvent>() {
@Override
public void handle(final ActionEvent paramT) {
final RadialMenuItem item = (RadialMenuItem) paramT.getSource();
System.out.println(item.getText());
}
};
this.radialMenu.addMenuItem(new RadialMenuItem(45, "forward", forward,
handler));
this.radialMenu.addMenuItem(new RadialMenuItem(45, "pause", pause,
handler));
this.radialMenu.addMenuItem(new RadialMenuItem(45, "play", play,
handler));
this.radialMenu.addMenuItem(new RadialMenuItem(45, "stop", stop,
handler));
this.radialMenu.addMenuItem(new RadialMenuItem(45, "backward",
backward, handler));
final GridPane controls = new GridPane();
controls.setVgap(15);
final Slider initialAngleSlider = SliderBuilder.create().min(-360)
.max(360).value(-23).minWidth(200).majorTickUnit(30)
.showTickLabels(true).build();
initialAngleSlider.valueProperty().addListener(
new ChangeListener<Number>() {
@Override
public void changed(
final ObservableValue<? extends Number> ov,
final Number old_val, final Number val) {
RadialMenuDemo.this.lastInitialAngleValue = val
.doubleValue();
RadialMenuDemo.this.radialMenu.setInitialAngle(val
.intValue());
System.out.println("Angle" + val);
}
});
controls.add(LabelBuilder.create().text("initial angle ").build(), 0, 1);
controls.add(initialAngleSlider, 1, 1);
final Slider innerRadiusSlider = SliderBuilder.create().min(0).max(300)
.value(30).minWidth(200).majorTickUnit(30).showTickLabels(true)
.build();
innerRadiusSlider.valueProperty().addListener(
new ChangeListener<Number>() {
@Override
public void changed(
final ObservableValue<? extends Number> ov,
final Number old_val, final Number new_val) {
RadialMenuDemo.this.radialMenu.setInnerRadius(new_val
.intValue());
}
});
controls.add(LabelBuilder.create().text("inner radius ").build(), 0, 3);
controls.add(innerRadiusSlider, 1, 3);
final Slider radiusSlider = SliderBuilder.create().min(100).max(500)
.value(100).minWidth(200).majorTickUnit(30)
.showTickLabels(true).build();
radiusSlider.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(final ObservableValue<? extends Number> ov,
final Number old_val, final Number new_val) {
RadialMenuDemo.this.radialMenu.setRadius(new_val.intValue());
}
});
controls.add(LabelBuilder.create().text("radius ").build(), 0, 4);
controls.add(radiusSlider, 1, 4);
final Slider offsetSlider = SliderBuilder.create().min(0).max(100)
.value(10).minWidth(200).majorTickUnit(30).showTickLabels(true)
.build();
offsetSlider.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(final ObservableValue<? extends Number> ov,
final Number old_val, final Number val) {
RadialMenuDemo.this.lastOffsetValue = val.doubleValue();
RadialMenuDemo.this.radialMenu.setOffset(val.intValue());
}
});
controls.add(LabelBuilder.create().text("offset ").build(), 0, 5);
controls.add(offsetSlider, 1, 5);
final ColorPicker colorPicker = ColorPickerBuilder.create()
.value(Color.LIGHTBLUE).build();
colorPicker.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(final ActionEvent t) {
RadialMenuDemo.this.radialMenu.setBackgroundColor(colorPicker
.getValue());
}
});
controls.add(LabelBuilder.create().text("backgroundColor ").build(), 0,
6);
controls.add(colorPicker, 1, 6);
final ColorPicker backgroundMouseOnPicker = ColorPickerBuilder.create()
.value(Color.LIGHTBLUE.darker()).build();
backgroundMouseOnPicker.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(final ActionEvent t) {
RadialMenuDemo.this.radialMenu
.setBackgroundMouseOnColor(backgroundMouseOnPicker
.getValue());
}
});
controls.add(LabelBuilder.create().text("background mouse on Color ")
.build(), 0, 7);
controls.add(backgroundMouseOnPicker, 1, 7);
final ColorPicker strokePicker = ColorPickerBuilder.create()
.value(Color.DARKBLUE).build();
strokePicker.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(final ActionEvent t) {
RadialMenuDemo.this.radialMenu.setStrokeColor(strokePicker
.getValue());
}
});
controls.add(LabelBuilder.create().text("stroke Color ").build(), 0, 8);
controls.add(strokePicker, 1, 8);
final CheckBox clockwiseCheckBox = CheckBoxBuilder.create()
.selected(false).build();
clockwiseCheckBox.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(final ActionEvent t) {
RadialMenuDemo.this.radialMenu.setClockwise(clockwiseCheckBox
.isSelected());
}
});
controls.add(LabelBuilder.create().text("clockwise ").build(), 0, 9);
controls.add(clockwiseCheckBox, 1, 9);
controls.translateXProperty().set(200);
controls.translateYProperty().set(-200);
return new Node[] { this.radialMenu, controls };
}
@Override
public void start(final Stage primaryStage) throws Exception {
this.container = new Group();
this.container.translateXProperty().set(150);
this.container.translateYProperty().set(250);
this.container.setCursor(Cursor.MOVE);
final Group root = new Group(this.container);
final Scene scene = new Scene(root);
scene.setFill(Color.WHITE);
primaryStage.setScene(scene);
primaryStage.setWidth(750);
primaryStage.setHeight(500);
primaryStage.setX(0);
primaryStage.setY(0);
primaryStage.setTitle("Radial Menu Demo");
primaryStage.show();
final Slider scaleSlider = SliderBuilder.create().min(0).max(20)
.value(1.0).minWidth(600).majorTickUnit(2).showTickLabels(true)
.build();
final GridPane zoom = new GridPane();
zoom.translateYProperty().set(20);
zoom.add(LabelBuilder.create().text("Scale ").build(), 0, 0);
zoom.add(scaleSlider, 1, 0);
this.container.scaleXProperty().bind(scaleSlider.valueProperty());
this.container.scaleYProperty().bind(scaleSlider.valueProperty());
root.getChildren().add(zoom);
// final Path axis = PathBuilder
// .create()
// .elements(new MoveTo(0, 0), new LineTo(0, 200),
// new LineTo(-10, 190), new MoveTo(0, 200),
// new LineTo(10, 190), new MoveTo(0, 200),
// new LineTo(0, 3000),
//
// new MoveTo(0, 0), new LineTo(200, 0),
// new LineTo(190, -10), new MoveTo(200, 0),
// new LineTo(190, 10), new MoveTo(200, 0),
// new LineTo(3000, 0)).stroke(Color.BLACK).build();
//
final Group lineGroup = new Group();
final int offset = 50;
final int nbLine = 100;
final Color lineColor = new Color(0.05, 0.05, 0.05, 0.5);
final Rectangle backgroundRectangle = RectangleBuilder.create()
.fill(Color.WHITE).x(-offset * nbLine).y(-offset * nbLine)
.width(offset * nbLine * 2).height(offset * nbLine * 2).build();
lineGroup.getChildren().add(backgroundRectangle);
for (int x = -nbLine; x < nbLine; x++) {
final Line line = LineBuilder.create().startX(x * offset)
.startY(-nbLine * offset).endX(x * offset)
.endY(nbLine * offset).stroke(lineColor).build();
lineGroup.getChildren().addAll(line);
}
for (int y = -nbLine; y < nbLine; y++) {
final Line line = LineBuilder.create().startX(-nbLine * offset)
.startY(y * offset).endX(nbLine * offset).endY(y * offset)
.stroke(lineColor).build();
lineGroup.getChildren().addAll(line);
}
// final Text origin = TextBuilder.create().text("(0,0)")
// .fill(Color.BLACK).x(-10).y(-5).build();
//
// final Text x = TextBuilder.create().text("X Axis").fill(Color.BLACK)
// .x(50).y(-5).build();
//
// final Text y = TextBuilder.create().text("Y Axis").fill(Color.BLACK)
// .x(-25).y(70).rotate(-90).build();
final AtomicBoolean dragging = new AtomicBoolean(false);
final EventHandler<MouseEvent> handler = new EventHandler<MouseEvent>() {
protected double pressX = 0.0d;
protected double pressY = 0.0d;
@Override
public void handle(final MouseEvent mouseEvent) {
if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
this.pressX = mouseEvent.getX();
this.pressY = mouseEvent.getY();
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {
dragging.set(true);
final double newX = RadialMenuDemo.this.container
.getTranslateX() + mouseEvent.getX() - this.pressX;
final double newY = RadialMenuDemo.this.container
.getTranslateY() + mouseEvent.getY() - this.pressY;
RadialMenuDemo.this.container.translateXProperty()
.set(newX);
RadialMenuDemo.this.container.translateYProperty()
.set(newY);
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_RELEASED) {
dragging.set(false);
}
}
};
this.container.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent event) {
if (!dragging.get()) {
if (event.getButton() == MouseButton.SECONDARY) {
RadialMenuDemo.this.showRadialMenu(event.getX(),
event.getY());
} else {
RadialMenuDemo.this.hideRadialMenu();
}
}
}
});
this.container.setOnMousePressed(handler);
this.container.setOnMouseReleased(handler);
this.container.setOnMouseDragged(handler);
RadialMenuDemo.this.container.getChildren().clear();
final Node[] dut = RadialMenuDemo.this.createShapes();
RadialMenuDemo.this.container.getChildren().add(lineGroup);
RadialMenuDemo.this.container.getChildren().addAll(dut);
// RadialMenuDemo.this.container.getChildren().addAll(axis, origin, x,
// y);
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(final WindowEvent paramT) {
RadialMenuDemo.this.executor.shutdownNow();
}
});
}
}