/* * */ package customnodes; import java.util.ArrayList; import application.Main; import application.tools.InfoTool; import javafx.animation.Animation; import javafx.animation.FadeTransition; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.application.Platform; import javafx.scene.Cursor; import javafx.scene.canvas.Canvas; import javafx.scene.control.Label; import javafx.scene.effect.DropShadow; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.input.ScrollEvent; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.shape.ArcType; import javafx.scene.shape.Circle; import javafx.scene.shape.StrokeLineCap; import javafx.scene.shape.StrokeLineJoin; import javafx.scene.text.TextAlignment; import javafx.scene.transform.Rotate; import javafx.util.Duration; import xplayer.visualizer.model.ResizableCanvas; /** * Represents a disc controller. * * @author GOXR3PLUS */ public class DJDisc extends StackPane { /** The Constant NULL_IMAGE. */ private static final Image NULL_IMAGE = InfoTool.getImageFromResourcesFolder("noImage.png"); /** The listeners. */ private final ArrayList<DJDiscListener> listeners = new ArrayList<>(); /** The arc color. */ private Color arcColor; /** * The Time will be Reversed or Normal. * * @author GOXR3PLUS */ private enum TimeMode { /** The normal. */ NORMAL, /** The reversed. */ REVERSED; } /** The angle. */ private int angle; /** The Constant MAXIMUM_VOLUME. */ private final int maximumVolume; /** The time. */ // About the Time private String time = "00:00"; /** The time mode. */ private TimeMode timeMode = TimeMode.NORMAL; /** The time field. */ private final Label timeField = new Label(time); /** The canvas. */ // Canvas private final ResizableCanvas canvas = new ResizableCanvas(); /** The rotation animation. */ // Animation private final Timeline rotationAnimation = new Timeline(); /** The fade. */ private final FadeTransition fade; /** The image view. */ private final ImageView imageView = new ImageView(); /** The volume label. */ private final DragAdjustableLabel volumeLabel; /** * The X of the Point that is in the circle circumference */ private double circlePointX; /** * The Y of the Point that is in the circle circumference */ private double circlePointY; /** * The rotation transformation */ private final Rotate rotationTransf; /** * Constructor. * * @param width * The width of the disc * @param height * The height of the disc * @param arcColor * The color of the disc arc * @param volume * The current volume of the disc * @param maximumVolume * The maximum volume of the disc [[SuppressWarningsSpartan]] */ public DJDisc(int width, int height, Color arcColor, int volume, int maximumVolume) { this.maximumVolume = maximumVolume; super.setPickOnBounds(true); // StackPane canvas.setPickOnBounds(false); canvas.setCursor(Cursor.OPEN_HAND); canvas.setPickOnBounds(false); super.setPickOnBounds(false); //setStyle("-fx-background-color:rgb(255,255,255,0.6)"); this.arcColor = arcColor; canvas.setEffect(new DropShadow(10, Color.WHITE)); // imageView replaceImage(null); // timeField timeField.setTextAlignment(TextAlignment.CENTER); timeField.setStyle( "-fx-background-color:white; -fx-padding:-2 8 -2 8; -fx-background-radius: 15; -fx-font-weight:bold; -fx-font-size:15; -fx-text-fill:black; -fx-cursor:hand;"); timeField.setOnMouseClicked(c -> { if (timeMode == TimeMode.NORMAL) { timeMode = TimeMode.REVERSED; timeField.setStyle( "-fx-background-color:white; -fx-padding:-2 8 -2 8; -fx-background-radius: 15; -fx-border-radius:15; -fx-border-color:red; -fx-border-width:2; -fx-font-weight:bold; -fx-font-size:15; -fx-text-fill:black; -fx-cursor:hand;"); } else { timeMode = TimeMode.NORMAL; timeField.setStyle( "-fx-background-color:white; -fx-padding:-2 8 -2 8; -fx-background-radius: 15; -fx-font-weight:bold; -fx-font-size:15; -fx-text-fill:black; -fx-cursor:hand;"); } }); // volumeLabel volumeLabel = new DragAdjustableLabel(volume, 0, maximumVolume); // volumeLabel.setFont(Font.loadFont(getClass().getResourceAsStream("/style/Younger // than me Bold.ttf"), 18)) volumeLabel.currentValueProperty().addListener((observable , oldValue , newValue) -> { listeners.forEach(l -> l.volumeChanged(newValue.intValue())); repaint(); }); // Fade animation for centerDisc fade = new FadeTransition(new Duration(1000), canvas); fade.setFromValue(1.0); fade.setToValue(0.2); fade.setAutoReverse(true); fade.setCycleCount(Animation.INDEFINITE); // fade.play() // rotation transform starting at 0 degrees, rotating about pivot point rotationTransf = new Rotate(0, width / 2.00 - 10, height / 2.00 - 10); imageView.getTransforms().add(rotationTransf); // rotate a square using time line attached to the rotation transform's // angle property. rotationAnimation.getKeyFrames().add(new KeyFrame(Duration.millis(2100), new KeyValue(rotationTransf.angleProperty(), 360))); rotationAnimation.setCycleCount(Animation.INDEFINITE); //rotationAnimation.play(); // When no album image exists this Label is shown Label noAlbumImageLabel = new Label("-Album Image-"); noAlbumImageLabel.setStyle("-fx-text-fill:white; -fx-font-weight:bold;"); noAlbumImageLabel.visibleProperty().bind(imageView.imageProperty().isEqualTo(NULL_IMAGE)); getChildren().addAll(canvas, imageView, noAlbumImageLabel, volumeLabel, timeField); // MouseListeners //canvas.setOnMousePressed(m -> canvas.setCursor(Cursor.CLOSED_HAND)) canvas.setOnMouseDragged(this::onMouseDragged); setOnScroll(this::onScroll); resizeDisc(width, height); } /** * Register a new DJDiscListener. * * @param listener * the listener */ public void addDJDiscListener(DJDiscListener listener) { listeners.add(listener); } /** * Resizes the disc to the given values. * * @param width1 * the width * @param height1 * the height */ public void resizeDisc(double width1 , double height1) { int width = (int) Math.round(width1); int height = width; //int height = (int) Math.round(height1) //System.out.println("Given:" + width1 + " , Rounded:" + width) if (width == height) if ( ( width >= 100 && height >= 100 ) && ( width % 2 == 0 && height % 2 == 0 )) { double halfWidth = width / 2.00 , halfHeight = height / 2.00; // {Maximum,Preferred} Size setMinSize(width, height); setMaxSize(width, height); setPrefSize(width, height); canvas.setWidth(width); canvas.setHeight(height); canvas.setClip(new Circle(halfWidth, halfHeight, halfWidth)); // ImageView int val = 8; imageView.setTranslateX(val); imageView.setTranslateY(val); imageView.setFitWidth(width - val * 2); imageView.setFitHeight(height - val * 2); imageView.setSmooth(true); imageView.setPreserveRatio(false); imageView.setClip(new Circle(halfWidth - val * 2, halfHeight - val * 2, halfWidth - val * 2)); // timeField timeField.setTranslateY(-height * 26 / 100.00); // volumeField volumeLabel.setTranslateY(+height * 26 / 100.00); //rotationTransformation rotationTransf.setPivotX(width / 2.00 - val * 2); rotationTransf.setPivotY(height / 2.00 - val * 2); repaint(); } else { Main.logger.info("DJDisc resizing failed.."); } } /** * Repaints the disc. */ public void repaint() { //Calculate here to use less cpu double prefWidth = getPrefWidth(); double prefHeight = getPrefHeight(); canvas.gc.setLineCap(StrokeLineCap.ROUND); //Clear the outer rectangle canvas.gc.clearRect(0, 0, prefWidth, prefHeight); canvas.gc.setFill(Color.WHITE); canvas.gc.fillRect(0, 0, prefWidth, prefHeight); // Arc Background Oval canvas.gc.setLineWidth(7); canvas.gc.setStroke(Color.WHITE); canvas.gc.strokeArc(5, 5, prefWidth - 10, prefHeight - 10, 90, 360.00 + angle, ArcType.OPEN); // Foreground Arc canvas.gc.setStroke(arcColor); canvas.gc.strokeArc(5, 5, prefWidth - 10, prefHeight - 10, 90, angle, ArcType.OPEN); // Volume Arc canvas.gc.setLineCap(StrokeLineCap.SQUARE); canvas.gc.setLineDashes(6); canvas.gc.setLineWidth(3); canvas.gc.setStroke(Color.FIREBRICK); int value = this.getVolume() == 0 ? 0 : (int) ( ( (double) this.getVolume() / (double) this.maximumVolume ) * 180 ); //System.out.println(value) canvas.gc.setFill(Color.BLACK); canvas.gc.fillArc(11, 11, prefWidth - 22, prefHeight - 22, 90, 360, ArcType.OPEN); canvas.gc.strokeArc(13, 13, prefWidth - 26, prefHeight - 26, -90, -value, ArcType.OPEN); canvas.gc.strokeArc(13, 13, prefWidth - 26, prefHeight - 26, -90,+value, ArcType.OPEN); canvas.gc.setLineDashes(0); canvas.gc.setLineCap(StrokeLineCap.ROUND); // --------------------------Maths to find the point on the circle // circumference // draw the progress oval // here i add + 89 to the angle cause the Java has where i have 0 // degrees the 90 degrees. // I am counting the 0 degrees from the top center of the circle and // Java calculates them from the right mid of the circle and it is // going left , i calculate them clock wise int angle2 = this.angle + 89; int minus = 8; // Find the point on the circle circumference circlePointX = Math.round( ( (int) ( prefWidth - minus ) ) / 2 + Math.cos(Math.toRadians(-angle2)) * ( (int) ( prefWidth - minus ) / 2 )); circlePointY = Math.round( ( (int) ( prefHeight - minus ) ) / 2 + Math.sin(Math.toRadians(-angle2)) * ( (int) ( prefHeight - minus ) / 2 )); // System.out.println("Width:" + canvas.getWidth() + " , Height:" + canvas.getHeight() + " , Angle: " + this.angle) // System.out.println(circlePointX + "," + circlePointY) int ovalWidth = 7; int ovalHeight = 7; // fix the circle position if (-angle >= 0 && -angle <= 90) { circlePointX = circlePointX - ovalWidth / 2 + 2; circlePointY = circlePointY + 1; } else if (-angle > 90 && -angle <= 180) { circlePointX = circlePointX - ovalWidth / 2 + 3; circlePointY = circlePointY - ovalWidth / 2 + 2; } else if (-angle > 180 && -angle <= 270) { circlePointX = circlePointX + 2; circlePointY = circlePointY - ovalWidth / 2 + 2; } else if (-angle > 270) { circlePointX = circlePointX + 2; // previousY = previousY - 7 } canvas.gc.setLineWidth(5); canvas.gc.setStroke(Color.BLACK); canvas.gc.strokeOval(circlePointX, circlePointY, ovalWidth, ovalHeight); canvas.gc.setFill(Color.MEDIUMVIOLETRED); canvas.gc.fillOval(circlePointX, circlePointY, ovalWidth, ovalHeight); // System.out.println("Angle is:" + ( -this.angle )) // System.out.println("FormatedX is: " + previousX + ", FormatedY is: " // + previousY) // System.out.println("Relative Mouse X: " + m.getX() + " , Relative // Mouse Y: " + m.getY()) // Draw the drag able rectangle // gc.setFill(Color.WHITE) // gc.fillOval(getWidth() / 2, 0, 20, 20) // ---------------------------------------------------------------------------------------------- // Refresh the timeField timeField.setText(time); } /** * @return The TimeMode of the TimeLabel */ public TimeMode getTimeMode() { return timeMode; } /** * Returns a Value based on the angle of the disc and the maximum value * allowed. * * @param maximum * the maximum * @return Returns a Value based on the angle of the disc and the maximum * value allowed */ public int getValue(int maximum) { return ( maximum * -angle ) / 360; } /** * Returns the volume of the disc. * * @return The Current Volume Value */ public int getVolume() { return volumeLabel.getCurrentValue(); } /** * Returns the maximum volume allowed. * * @return The Maximum Volume allowed in the Disc */ public int getMaximumVolume() { return maximumVolume; } /** * Returns the color of the arc. * * @return The Color of the Disc Arc */ public Color getArcColor() { return arcColor; } /** * Returns the Canvas of the disc. * * @return The Canvas of the Disc */ public Canvas getCanvas() { return canvas; } /** * Calculates the angle based on the given value and the maximum value * allowed. * * @param value * The current moment of the Audio * @param maximum * The maximum duration of the Audio * @param updateTheTime * True if you want to update the Time Label */ public void calculateAngleByValue(int value , int maximum , boolean updateTheTime) { // or else i get // java.lang.ArithmeticException: / by zero if (maximum != 0) { // threshold values if (value == 0) angle = 0; else if (value == maximum) angle = 360; else// calculate angle = - ( ( value * 360 ) / maximum ); } else angle = 0; //Update the Time? if (updateTheTime) calculateTheTime(value, maximum); } /** * Calculates the time of the disc. * * @param current * the current * @param total * the total */ private void calculateTheTime(int current , int total) { if (current == 0 && total == 0) time = "00:00"; else if (timeMode == TimeMode.NORMAL) time = InfoTool.getTimeEdited(current); else time = "-" + InfoTool.getTimeEdited(total - current); } /** * Update the time of the disc directly using this method * * @param current * @param total * @param milliseconds */ public void updateTimeDirectly(int current , int total , String milliseconds) { calculateTheTime(current, total); if (!this.timeField.isHover()) { //Is being hovered if (timeMode == TimeMode.REVERSED) this.time = time + "." + ( 9 - Integer.parseInt(milliseconds.replace(".", "")) ); else this.time = time + milliseconds; } else this.time = InfoTool.getTimeEdited(total); //Final //this.time = time + "\n," + InfoTool.getTimeEdited(total); } /** * Change the volume. * * @param volume * the new volume */ public void setVolume(int volume) { if (volume > -1 && volume < getMaximumVolume() + 1) Platform.runLater(() -> volumeLabel.setCurrentValue(volume)); else if (volume < 0) Platform.runLater(() -> volumeLabel.setCurrentValue(0)); else if (volume > getMaximumVolume()) Platform.runLater(() -> volumeLabel.setCurrentValue(getMaximumVolume())); } /** * Set the color of the arc to the given one. * * @param color * the new arc color */ public void setArcColor(Color color) { arcColor = color; } /** * Replace the image of the disc with the given one. * * @param image * the image */ public void replaceImage(Image image) { if (image != null) imageView.setImage(image); else imageView.setImage(NULL_IMAGE); } /** * Resume the rotation of the disc. */ public void resumeRotation() { rotationAnimation.play(); } /** * Pause the rotation of the disc. */ public void pauseRotation() { rotationAnimation.pause(); } /** * Stops the Rotation Animation */ public void stopRotation() { rotationAnimation.jumpTo(Duration.ZERO); rotationAnimation.pause(); } /** * Start the fade effect of the disc. */ public void playFade() { fade.play(); } /** * Stop fade effect of the disc. */ public void stopFade() { fade.jumpTo(Duration.ZERO); fade.pause(); } /** * Calculate the disc angle based on mouse position. * * @param m * the m * @param current * the current * @param total * the total */ public void calculateAngleByMouse(MouseEvent m , int current , int total) { // pauseRotation() double mouseX; double mouseY; if (m.getButton() == MouseButton.PRIMARY || m.getButton() == MouseButton.SECONDARY) { mouseX = m.getX(); mouseY = m.getY(); if (mouseX > getWidth() / 2) angle = (int) Math.toDegrees(Math.atan2(getWidth() / 2 - mouseX, getHeight() / 2 - mouseY)); else { angle = (int) Math.toDegrees(Math.atan2(getWidth() / 2 - mouseX, getHeight() / 2 - mouseY)); angle = - ( 360 - angle ); // make it minus cause i turn it // on the right } // System.out.println(-angle) calculateTheTime(current, total); // rotationTransform.setAngle(-angle) Platform.runLater(this::repaint); } } /** * Check if this point is contained into the circle * * @param mouseX * @param mouseY * @return */ @SuppressWarnings("unused") private boolean isContainedInCircle(double mouseX , double mouseY) { // Check if it is contained into the circle if (Math.sqrt(Math.pow( ( (int) ( getWidth() - 5 ) / 2 ) - (int) mouseX, 2) + Math.pow( ( (int) ( getHeight() - 5 ) / 2 ) - (int) mouseY, 2)) <= Math.floorDiv((int) ( getWidth() - 5 ), 2)) { System.out.println("The point is contained in the circle."); return true; } else { System.out.println("The point is not contained in the circle."); return false; } } /** * On mouse dragged. * * @param m * the m */ private void onMouseDragged(MouseEvent m) { calculateAngleByMouse(m, 0, 0); } /** * On scroll. * * @param m * the m */ private void onScroll(ScrollEvent m) { int rotation = m.getDeltaY() < 1 ? 1 : -1; setVolume(getVolume() - rotation); } }