/*
* RadialMenuItem
* 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 javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Paint;
import javafx.scene.shape.ArcTo;
import javafx.scene.shape.FillRule;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
public class RadialMenuItem extends Group {
double getStartAngle() {
return this.startAngle;
}
void setStartAngle(final double startAngle) {
this.startAngle = startAngle;
this.drawMenuItem();
}
double getInnerRadius() {
return this.innerRadius;
}
void setInnerRadius(final double innerRadius) {
this.innerRadius = innerRadius;
this.drawMenuItem();
}
double getRadius() {
return this.radius;
}
void setRadius(final double radius) {
this.radius = radius;
this.drawMenuItem();
}
double getOffset() {
return this.offset;
}
void setOffset(final double offset) {
this.offset = offset;
this.drawMenuItem();
}
Paint getBackgroundMouseOnColor() {
return this.backgroundMouseOnColor;
}
void setBackgroundMouseOnColor(final Paint color) {
this.backgroundMouseOnColor = color;
this.drawMenuItem();
}
Paint getBackgroundColor() {
return this.backgroundColor;
}
void setBackgroundColor(final Paint backgroundColor) {
this.backgroundColor = backgroundColor;
this.drawMenuItem();
}
boolean isClockwise() {
return this.clockWise;
}
void setClockwise(final boolean clockWise) {
this.clockWise = clockWise;
this.drawMenuItem();
}
Paint getStrokeColor() {
return this.strokeColor;
}
void setStrokeColor(final Paint color) {
this.strokeColor = color;
this.drawMenuItem();
}
void setBackgroundVisible(final boolean visible) {
this.backgroundVisible = visible;
this.drawMenuItem();
}
boolean isBackgroundVisible() {
return this.backgroundVisible;
}
void setStrokeVisible(final boolean visible) {
this.strokeVisible = visible;
this.drawMenuItem();
}
boolean isStrokeVisible() {
return this.strokeVisible;
}
public Node getGraphic() {
return this.graphic;
}
public void setGraphic(final Node graphic) {
if (this.graphic != null) {
this.getChildren().remove(graphic);
}
this.graphic = graphic;
if (this.graphic != null) {
this.getChildren().add(graphic);
}
this.drawMenuItem();
}
public void setText(final String text) {
this.text = text;
this.drawMenuItem();
}
public String getText() {
return this.text;
}
protected double startAngle;
protected double menuSize;
protected double innerRadius;
protected double radius;
protected double offset;
protected Paint computedBackgroundFill;
protected Paint backgroundMouseOnColor;
protected Paint backgroundColor;
protected boolean backgroundVisible = true;
protected boolean strokeVisible = true;
protected boolean clockWise;
protected boolean northBased;
protected Paint strokeColor;
protected MoveTo moveTo;
protected ArcTo arcToInner;
protected ArcTo arcTo;
protected LineTo lineTo;
protected LineTo lineTo2;
protected double innerStartX;
protected double innerStartY;
protected double innerEndX;
protected double innerEndY;
protected boolean innerSweep;
protected double startX;
protected double startY;
protected double endX;
protected double endY;
protected boolean sweep;
protected double graphicX;
protected double graphicY;
protected double translateX;
protected double translateY;
protected boolean mouseOn = false;
protected Path path;
protected Node graphic;
protected String text;
public RadialMenuItem() {
this.path = new Path();
this.moveTo = new MoveTo();
this.arcToInner = new ArcTo();
this.arcTo = new ArcTo();
this.lineTo = new LineTo();
this.lineTo2 = new LineTo();
this.path.getElements().add(this.moveTo);
this.path.getElements().add(this.arcToInner);
this.path.getElements().add(this.lineTo);
this.path.getElements().add(this.arcTo);
this.path.getElements().add(this.lineTo2);
this.getChildren().add(this.path);
this.setOnMouseEntered(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent arg0) {
RadialMenuItem.this.mouseOn = true;
RadialMenuItem.this.drawMenuItem();
}
});
this.setOnMouseExited(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent arg0) {
RadialMenuItem.this.mouseOn = false;
RadialMenuItem.this.drawMenuItem();
}
});
}
public RadialMenuItem(final double menuSize, final Node graphic) {
this();
this.menuSize = menuSize;
this.graphic = graphic;
if (this.graphic != null)
this.getChildren().add(this.graphic);
this.drawMenuItem();
}
public RadialMenuItem(final double menuSize, final Node graphic,
final EventHandler<ActionEvent> actionHandler) {
this(menuSize, graphic);
this.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent paramT) {
actionHandler.handle(new ActionEvent(paramT.getSource(), paramT
.getTarget()));
}
});
this.drawMenuItem();
}
public RadialMenuItem(final double menuSize, final String text,
final Node graphic, final EventHandler<ActionEvent> actionHandler) {
this(menuSize, graphic, actionHandler);
this.text = text;
this.drawMenuItem();
}
protected void drawMenuItem() {
this.path
.setFill(this.backgroundVisible ? (this.mouseOn
&& this.backgroundMouseOnColor != null ? this.backgroundMouseOnColor
: this.backgroundColor)
: null);
this.path.setStroke(this.strokeVisible ? this.strokeColor : null);
this.path.setFillRule(FillRule.EVEN_ODD);
this.computeCoordinates();
this.update();
}
protected void update() {
this.moveTo.setX(this.innerStartX);
this.moveTo.setY(this.innerStartY);
this.arcToInner.setX(this.innerEndX);
this.arcToInner.setY(this.innerEndY);
this.arcToInner.setSweepFlag(this.innerSweep);
this.arcToInner.setRadiusX(this.innerRadius);
this.arcToInner.setRadiusY(this.innerRadius);
this.lineTo.setX(this.startX);
this.lineTo.setY(this.startY);
this.arcTo.setX(this.endX);
this.arcTo.setY(this.endY);
this.arcTo.setSweepFlag(this.sweep);
this.arcTo.setRadiusX(this.radius);
this.arcTo.setRadiusY(this.radius);
this.lineTo2.setX(this.innerStartX);
this.lineTo2.setY(this.innerStartY);
if (this.graphic != null) {
this.graphic.setTranslateX(this.graphicX);
this.graphic.setTranslateY(this.graphicY);
}
this.translateXProperty().set(this.translateX);
this.translateYProperty().set(this.translateY);
}
protected void computeCoordinates() {
final double graphicAngle = (this.startAngle) + (this.menuSize / 2.0);
final double graphicRadius = this.innerRadius
+ (this.radius - this.innerRadius) / 2.0;
if (!this.clockWise) {
this.innerStartX = this.innerRadius
* Math.cos(Math.toRadians(this.startAngle));
this.innerStartY = -this.innerRadius
* Math.sin(Math.toRadians(this.startAngle));
this.innerEndX = this.innerRadius
* Math.cos(Math.toRadians(this.startAngle + this.menuSize));
this.innerEndY = -this.innerRadius
* Math.sin(Math.toRadians(this.startAngle + this.menuSize));
this.innerSweep = false;
this.startX = this.radius
* Math.cos(Math.toRadians(this.startAngle + this.menuSize));
this.startY = -this.radius
* Math.sin(Math.toRadians(this.startAngle + this.menuSize));
this.endX = this.radius * Math.cos(Math.toRadians(this.startAngle));
this.endY = -this.radius
* Math.sin(Math.toRadians(this.startAngle));
this.sweep = true;
this.graphicX = graphicRadius
* Math.cos(Math.toRadians(graphicAngle))
- this.graphic.getBoundsInParent().getWidth() / 2;
this.graphicY = -graphicRadius
* Math.sin(Math.toRadians(graphicAngle))
- this.graphic.getBoundsInParent().getHeight() / 2.0;
this.translateX = this.offset
* Math.cos(Math.toRadians(this.startAngle
+ (this.menuSize / 2.0)));
this.translateY = -this.offset
* Math.sin(Math.toRadians(this.startAngle
+ (this.menuSize / 2.0)));
} else if (this.clockWise) {
this.innerStartX = this.innerRadius
* Math.cos(Math.toRadians(this.startAngle));
this.innerStartY = this.innerRadius
* Math.sin(Math.toRadians(this.startAngle));
this.innerEndX = this.innerRadius
* Math.cos(Math.toRadians(this.startAngle + this.menuSize));
this.innerEndY = this.innerRadius
* Math.sin(Math.toRadians(this.startAngle + this.menuSize));
this.innerSweep = true;
this.startX = this.radius
* Math.cos(Math.toRadians(this.startAngle + this.menuSize));
this.startY = this.radius
* Math.sin(Math.toRadians(this.startAngle + this.menuSize));
this.endX = this.radius * Math.cos(Math.toRadians(this.startAngle));
this.endY = this.radius * Math.sin(Math.toRadians(this.startAngle));
this.sweep = false;
this.graphicX = graphicRadius
* Math.cos(Math.toRadians(graphicAngle));
this.graphicY = graphicRadius
* Math.sin(Math.toRadians(graphicAngle));
this.translateX = this.offset
* Math.cos(Math.toRadians(this.startAngle
+ (this.menuSize / 2.0)));
this.translateY = this.offset
* Math.sin(Math.toRadians(this.startAngle + this.startAngle
+ (this.menuSize / 2.0)));
}
}
public double getMenuSize() {
return this.menuSize;
}
}