Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.jfoenix.skins.JFXTimePickerContent Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.jfoenix.skins;
import com.jfoenix.controls.JFXTimePicker;
import javafx.animation.*;
import javafx.beans.property.*;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;
import javafx.util.converter.LocalTimeStringConverter;
import javafx.util.converter.NumberStringConverter;
import java.awt.geom.Point2D;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;
/**
* @author Shadi Shaheen
*/
public class JFXTimePickerContent extends VBox {
private enum TimeUnit {HOURS, MINUTES}
private static final String ROBOTO = "Roboto";
private static final String SPINNER_LABEL = "spinner-label";
protected JFXTimePicker timePicker;
private Color fadedColor = Color.rgb(255, 255, 255, 0.67);
private boolean is24HourView = false;
private double contentCircleRadius = 100;
private Label selectedHourLabel = new Label();
private Label selectedMinLabel = new Label();
private Label periodPMLabel, periodAMLabel;
private StackPane calendarPlaceHolder = new StackPane();
private StackPane hoursContent;
private StackPane minutesContent;
private Rotate hoursPointerRotate, _24HourHoursPointerRotate;
private Rotate minsPointerRotate;
private ObjectProperty unit = new SimpleObjectProperty<>(TimeUnit.HOURS);
private DoubleProperty angle = new SimpleDoubleProperty(Math.toDegrees(2 * Math.PI / 12));
private StringProperty period = new SimpleStringProperty("AM");
private ObjectProperty pointerRotate = new SimpleObjectProperty<>(),
_24HourPointerRotate = new SimpleObjectProperty<>();
private ObjectProperty timeLabel = new SimpleObjectProperty<>();
private NumberStringConverter unitConverter = new NumberStringConverter("#00");
private ObjectProperty selectedTime = new SimpleObjectProperty<>(this, "selectedTime");
JFXTimePickerContent(final JFXTimePicker jfxTimePicker) {
this.timePicker = jfxTimePicker;
LocalTime time = this.timePicker.getValue() == null ?
LocalTime.now() : this.timePicker.getValue();
is24HourView = this.timePicker.is24HourView();
this.timePicker.valueProperty().addListener((o, oldVal, newVal) -> goToTime(newVal));
getStyleClass().add("date-picker-popup");
// create the header pane
getChildren().add(createHeaderPane(time, is24HourView));
VBox contentHolder = new VBox();
// create content pane
contentHolder.getChildren().add(createContentPane(time, is24HourView));
calendarPlaceHolder.getChildren().add(contentHolder);
Rectangle clip = new Rectangle();
clip.widthProperty().bind(calendarPlaceHolder.widthProperty());
clip.heightProperty().bind(calendarPlaceHolder.heightProperty());
calendarPlaceHolder.setClip(clip);
StackPane contentPlaceHolder = new StackPane(calendarPlaceHolder);
getChildren().add(contentPlaceHolder);
// add listeners
unit.addListener((o, oldVal, newVal) -> {
if (newVal == TimeUnit.HOURS) {
angle.set(Math.toDegrees(2 * Math.PI / 12));
int tmp = Integer.parseInt(selectedHourLabel.getText());
if (is24HourView) {
if (tmp == 0 || tmp > 12) {
hoursContent.getChildren().get(0).setVisible(false);
hoursContent.getChildren().get(1).setVisible(true);
} else {
hoursContent.getChildren().get(1).setVisible(false);
hoursContent.getChildren().get(0).setVisible(true);
}
}
pointerRotate.set(_24HourHoursPointerRotate);
_24HourPointerRotate.set(_24HourHoursPointerRotate);
timeLabel.set(selectedHourLabel);
} else if (newVal == TimeUnit.MINUTES) {
angle.set(Math.toDegrees(2 * Math.PI / 60));
pointerRotate.set(minsPointerRotate);
timeLabel.set(selectedMinLabel);
}
swapLabelsColor(selectedHourLabel, selectedMinLabel);
switchTimeUnit(newVal);
});
if (!is24HourView) {
period.addListener((o, oldVal, newVal) -> {
swapLabelsColor(periodPMLabel, periodAMLabel);
updateValue();
});
}
}
protected BorderPane createContentPane(LocalTime time, boolean _24HourView) {
Circle circle = new Circle(contentCircleRadius),
selectionCircle = new Circle(contentCircleRadius / 6);
circle.setFill(Color.rgb(224, 224, 224, 0.67));
EventHandler super MouseEvent> mouseActionHandler = (event) -> {
double dx = event.getX();
double dy = event.getY();
double shift = 9;
double theta = Math.atan2(dy, dx);
int index = (int) Math.round((180 + Math.toDegrees(theta)) / angle.get()),
timeValue;
if (_24HourView) {
if (Point2D.distance(0, 0, dx, dy) >= (contentCircleRadius - shift - (2 * selectionCircle.getRadius()))) {
hoursContent.getChildren().get(1).setVisible(false);
hoursContent.getChildren().get(0).setVisible(true);
pointerRotate.get().setAngle(index * angle.get());
timeValue = (index + 9) % 12 == 0 ? 12 : (index + 9) % 12;
} else {
hoursContent.getChildren().get(0).setVisible(false);
hoursContent.getChildren().get(1).setVisible(true);
_24HourPointerRotate.get().setAngle(index * angle.get());
int tmp = ((index + 21) % 24 <= 13 ? (index + 21) % 24 + 12 : (index + 21) % 24);
timeValue = tmp == 12 ? 0 : tmp;
}
} else {
pointerRotate.get().setAngle(index * angle.get());
timeValue = (index + 9) % 12 == 0 ? 12 : (index + 9) % 12;
}
if (unit.get() == TimeUnit.MINUTES) {
timeValue = (index + 45) % 60;
}
timeLabel.get().setText(unit.get() == TimeUnit.MINUTES ? unitConverter.toString(timeValue) : Integer.toString(timeValue));
updateValue();
};
circle.setOnMousePressed(mouseActionHandler);
circle.setOnMouseDragged(mouseActionHandler);
hoursContent = createHoursContent(time, _24HourView);
hoursContent.setMouseTransparent(true);
minutesContent = createMinutesContent(time);
minutesContent.setOpacity(0);
minutesContent.setMouseTransparent(true);
StackPane contentPane = new StackPane();
contentPane.getChildren().addAll(circle, hoursContent, minutesContent);
contentPane.setPadding(new Insets(12));
BorderPane contentContainer = new BorderPane();
contentContainer.setCenter(contentPane);
contentContainer.setMinHeight(50);
contentContainer.setPadding(new Insets(2, 12, 2, 12));
return contentContainer;
}
/*
* header panel represents the selected Time
* we keep javaFX original style classes
*/
protected StackPane createHeaderPane(LocalTime time, boolean _24HourView) {
int hour = time.getHour();
selectedHourLabel.setText((hour % (_24HourView ? 24 : 12) == 0 ? (_24HourView ? 0 : 12) : hour % (_24HourView ? 24 : 12)) + "");
selectedHourLabel.getStyleClass().add(SPINNER_LABEL);
selectedHourLabel.setTextFill(Color.WHITE);
selectedHourLabel.setFont(Font.font(ROBOTO, FontWeight.BOLD, 42));
selectedHourLabel.setOnMouseClicked((click) -> unit.set(TimeUnit.HOURS));
selectedHourLabel.setMinWidth(49);
selectedHourLabel.setAlignment(Pos.CENTER_RIGHT);
timeLabel.set(selectedHourLabel);
selectedMinLabel.setText(unitConverter.toString(time.getMinute()) + "");
selectedMinLabel.getStyleClass().add(SPINNER_LABEL);
selectedMinLabel.setTextFill(fadedColor);
selectedMinLabel.setFont(Font.font(ROBOTO, FontWeight.BOLD, 42));
selectedMinLabel.setOnMouseClicked((click) -> unit.set(TimeUnit.MINUTES));
Label separatorLabel = new Label(":");
separatorLabel.setPadding(new Insets(0, 0, 4, 0));
separatorLabel.setTextFill(fadedColor);
separatorLabel.setFont(Font.font(ROBOTO, FontWeight.BOLD, 42));
periodPMLabel = new Label("PM");
periodPMLabel.getStyleClass().add(SPINNER_LABEL);
periodPMLabel.setTextFill(fadedColor);
periodPMLabel.setFont(Font.font(ROBOTO, FontWeight.BOLD, 14));
periodPMLabel.setOnMouseClicked((click) -> period.set("PM"));
periodAMLabel = new Label("AM");
periodAMLabel.getStyleClass().add(SPINNER_LABEL);
periodAMLabel.setTextFill(fadedColor);
periodAMLabel.setFont(Font.font(ROBOTO, FontWeight.BOLD, 14));
periodAMLabel.setOnMouseClicked((click) -> period.set("AM"));
// init period value
if (hour < 12) {
periodAMLabel.setTextFill(Color.WHITE);
} else {
periodPMLabel.setTextFill(Color.WHITE);
}
period.set(hour < 12 ? "AM" : "PM");
VBox periodContainer = new VBox();
periodContainer.setPadding(new Insets(0, 0, 0, 4));
periodContainer.getChildren().addAll(periodAMLabel, periodPMLabel);
// Year label container
HBox selectedTimeContainer = new HBox();
selectedTimeContainer.getStyleClass().add("spinner");
selectedTimeContainer.getChildren()
.addAll(selectedHourLabel, separatorLabel, selectedMinLabel);
if (!_24HourView) {
selectedTimeContainer.getChildren().add(periodContainer);
}
selectedTimeContainer.setAlignment(Pos.CENTER);
selectedTimeContainer.setFillHeight(false);
StackPane headerPanel = new StackPane();
headerPanel.getStyleClass().add("time-pane");
headerPanel.setBackground(new Background(new BackgroundFill(this.timePicker.getDefaultColor(),
CornerRadii.EMPTY,
Insets.EMPTY)));
headerPanel.setPadding(new Insets(8, 24, 8, 24));
headerPanel.getChildren().add(selectedTimeContainer);
return headerPanel;
}
private StackPane createHoursContent(LocalTime time, boolean _24HourView) {
// create hours content
StackPane hoursPointer = new StackPane(), _24HoursPointer = new StackPane();
Circle selectionCircle = new Circle(contentCircleRadius / 6),
_24HourSelectionCircle = new Circle(contentCircleRadius / 6);
selectionCircle.fillProperty().bind(timePicker.defaultColorProperty());
_24HourSelectionCircle.fillProperty().bind(timePicker.defaultColorProperty());
double shift = 9, _24HourShift = 27.5;
Line line = new Line(shift, 0, contentCircleRadius, 0);
line.fillProperty().bind(timePicker.defaultColorProperty());
line.strokeProperty().bind(line.fillProperty());
line.setStrokeWidth(1.5);
hoursPointer.getChildren().addAll(line, selectionCircle);
StackPane.setAlignment(selectionCircle, Pos.CENTER_LEFT);
Group pointerGroup = new Group();
pointerGroup.getChildren().add(hoursPointer);
pointerGroup.setTranslateX((-contentCircleRadius + shift) / 2);
hoursPointerRotate = new Rotate(0, contentCircleRadius - shift, selectionCircle.getRadius());
pointerRotate.set(hoursPointerRotate);
pointerGroup.getTransforms().add(hoursPointerRotate);
pointerGroup.setVisible(!is24HourView);
Line _24HourLine = new Line(shift + _24HourShift, 0, contentCircleRadius, 0);
_24HourLine.fillProperty().bind(timePicker.defaultColorProperty());
_24HourLine.strokeProperty().bind(_24HourLine.fillProperty());
_24HourLine.setStrokeWidth(1.5);
_24HoursPointer.getChildren().addAll(_24HourLine, _24HourSelectionCircle);
StackPane.setAlignment(_24HourSelectionCircle, Pos.CENTER_LEFT);
Group pointer24HourGroup = new Group();
pointer24HourGroup.getChildren().add(_24HoursPointer);
pointer24HourGroup.setTranslateX(((-contentCircleRadius + shift) / 2) + (_24HourShift / 2));
_24HourHoursPointerRotate = new Rotate(0, contentCircleRadius - shift - _24HourShift, selectionCircle.getRadius());
_24HourPointerRotate.set(_24HourHoursPointerRotate);
pointer24HourGroup.getTransforms().add(_24HourHoursPointerRotate);
pointer24HourGroup.setVisible(is24HourView);
Pane clockLabelsContainer = new Pane();
// inner circle radius
double radius = contentCircleRadius - shift - selectionCircle.getRadius();
for (int i = 0; i < 12; i++) {
// create the label and its container
int val = (i + 3) % 12 == 0 ? 12 : (i + 3) % 12;
Label label = new Label(Integer.toString(val));
label.setFont(Font.font(ROBOTO, FontWeight.BOLD, 12));
// init color
label.setTextFill(((val == time.getHour() % 12 || (val == 12 && time.getHour() % 12 == 0)) && !is24HourView) ?
Color.rgb(255, 255, 255, 0.87) : Color.rgb(0, 0, 0, 0.87));
selectedHourLabel.textProperty().addListener((o, oldVal, newVal) -> {
if (Integer.parseInt(newVal) == Integer.parseInt(label.getText())) {
label.setTextFill(Color.rgb(255, 255, 255, 0.87));
} else {
label.setTextFill(Color.rgb(0, 0, 0, 0.87));
}
});
// create label container
StackPane labelContainer = new StackPane();
labelContainer.getChildren().add(label);
double labelSize = (selectionCircle.getRadius() / Math.sqrt(2)) * 2;
labelContainer.setMinSize(labelSize, labelSize);
// position the label on the circle
double angle = 2 * i * Math.PI / 12;
double xOffset = radius * Math.cos(angle);
double yOffset = radius * Math.sin(angle);
final double startx = contentCircleRadius + xOffset;
final double starty = contentCircleRadius + yOffset;
labelContainer.setLayoutX(startx - labelContainer.getMinWidth() / 2);
labelContainer.setLayoutY(starty - labelContainer.getMinHeight() / 2);
// add label to the parent node
clockLabelsContainer.getChildren().add(labelContainer);
// init pointer angle
if (!is24HourView && (val == time.getHour() % 12 || (val == 12 && time.getHour() % 12 == 0))) {
hoursPointerRotate.setAngle(180 + Math.toDegrees(angle));
}
}
if (_24HourView) {
radius /= 1.6;
for (int i = 0; i < 12; i++) {
// create the label and its container
int val = (i + 3) % 12 == 0 ? 12 : (i + 3) % 12;
val += (val == 12 ? -12 : 12);
Label label = new Label(Integer.toString(val) + (val == 0 ? "0" : ""));
label.setFont(Font.font(ROBOTO, FontWeight.NORMAL, 10));
// init color
label.setTextFill((val == time.getHour() % 24 || (val == 0 && time.getHour() % 24 == 0) && is24HourView) ?
Color.rgb(255, 255, 255, 0.54) : Color.rgb(0, 0, 0, 0.54));
selectedHourLabel.textProperty().addListener((o, oldVal, newVal) -> {
if (Integer.parseInt(newVal) == Integer.parseInt(label.getText())) {
label.setTextFill(Color.rgb(255, 255, 255, 0.54));
} else {
label.setTextFill(Color.rgb(0, 0, 0, 0.54));
}
});
// create label container
StackPane labelContainer = new StackPane();
labelContainer.getChildren().add(label);
double labelSize = (selectionCircle.getRadius() / Math.sqrt(2)) * 2;
labelContainer.setMinSize(labelSize, labelSize);
// position the label on the circle
double angle = 2 * i * Math.PI / 12;
double xOffset = radius * Math.cos(angle);
double yOffset = radius * Math.sin(angle);
final double startx = contentCircleRadius + xOffset;
final double starty = contentCircleRadius + yOffset;
labelContainer.setLayoutX(startx - labelContainer.getMinWidth() / 2);
labelContainer.setLayoutY(starty - labelContainer.getMinHeight() / 2);
// add label to the parent node
clockLabelsContainer.getChildren().add(labelContainer);
// init pointer angle
if (val == time.getHour() % 24 || (val == 24 && time.getHour() % 24 == 0)) {
_24HourHoursPointerRotate.setAngle(180 + Math.toDegrees(angle));
}
}
}
if (_24HourView) {
return new StackPane(pointerGroup, pointer24HourGroup, clockLabelsContainer);
} else {
return new StackPane(pointerGroup, clockLabelsContainer);
}
}
private StackPane createMinutesContent(LocalTime time) {
// create minutes content
StackPane minsPointer = new StackPane();
Circle selectionCircle = new Circle(contentCircleRadius / 6);
selectionCircle.fillProperty().bind(timePicker.defaultColorProperty());
Circle minCircle = new Circle(selectionCircle.getRadius() / 8);
minCircle.setFill(Color.rgb(255, 255, 255, 0.87));
minCircle.setTranslateX(selectionCircle.getRadius() - minCircle.getRadius());
minCircle.setVisible(time.getMinute() % 5 != 0);
selectedMinLabel.textProperty().addListener((o, oldVal, newVal) -> {
if (Integer.parseInt(newVal) % 5 == 0) {
minCircle.setVisible(false);
} else {
minCircle.setVisible(true);
}
});
double shift = 9;
Line line = new Line(shift, 0, contentCircleRadius, 0);
line.fillProperty().bind(timePicker.defaultColorProperty());
line.strokeProperty().bind(line.fillProperty());
line.setStrokeWidth(1.5);
minsPointer.getChildren().addAll(line, selectionCircle, minCircle);
StackPane.setAlignment(selectionCircle, Pos.CENTER_LEFT);
StackPane.setAlignment(minCircle, Pos.CENTER_LEFT);
Group pointerGroup = new Group();
pointerGroup.getChildren().add(minsPointer);
pointerGroup.setTranslateX((-contentCircleRadius + shift) / 2);
minsPointerRotate = new Rotate(0, contentCircleRadius - shift, selectionCircle.getRadius());
pointerGroup.getTransforms().add(minsPointerRotate);
Pane clockLabelsContainer = new Pane();
// inner circle radius
double radius = contentCircleRadius - shift - selectionCircle.getRadius();
for (int i = 0; i < 12; i++) {
StackPane labelContainer = new StackPane();
int val = ((i + 3) * 5) % 60;
Label label = new Label(unitConverter.toString(val) + "");
label.setFont(Font.font(ROBOTO, FontWeight.BOLD, 12));
// init label color
label.setTextFill(val == time.getMinute() ?
Color.rgb(255, 255, 255, 0.87) : Color.rgb(0, 0, 0, 0.87));
selectedMinLabel.textProperty().addListener((o, oldVal, newVal) -> {
if (Integer.parseInt(newVal) == Integer.parseInt(label.getText())) {
label.setTextFill(Color.rgb(255, 255, 255, 0.87));
} else {
label.setTextFill(Color.rgb(0, 0, 0, 0.87));
}
});
labelContainer.getChildren().add(label);
double labelSize = (selectionCircle.getRadius() / Math.sqrt(2)) * 2;
labelContainer.setMinSize(labelSize, labelSize);
double angle = 2 * i * Math.PI / 12;
double xOffset = radius * Math.cos(angle);
double yOffset = radius * Math.sin(angle);
final double startx = contentCircleRadius + xOffset;
final double starty = contentCircleRadius + yOffset;
labelContainer.setLayoutX(startx - labelContainer.getMinWidth() / 2);
labelContainer.setLayoutY(starty - labelContainer.getMinHeight() / 2);
// add label to the parent node
clockLabelsContainer.getChildren().add(labelContainer);
}
minsPointerRotate.setAngle(180 + (time.getMinute() + 45) % 60 * Math.toDegrees(2 * Math.PI / 60));
return new StackPane(pointerGroup, clockLabelsContainer);
}
ObjectProperty displayedTimeProperty() {
return selectedTime;
}
void init() {
calendarPlaceHolder.setOpacity(1);
if(unit.get() == TimeUnit.HOURS){
selectedHourLabel.setTextFill(Color.rgb(255, 255, 255, 0.87));
}else{
selectedMinLabel.setTextFill(Color.rgb(255, 255, 255, 0.87));
}
}
private void swapLabelsColor(Label lbl1, Label lbl2) {
Paint color = lbl1.getTextFill();
lbl1.setTextFill(lbl2.getTextFill());
lbl2.setTextFill(color);
}
private void switchTimeUnit(TimeUnit newVal) {
if (newVal == TimeUnit.HOURS) {
Timeline fadeout = new Timeline(new KeyFrame(Duration.millis(320),
new KeyValue(minutesContent.opacityProperty(),
0,
Interpolator.EASE_BOTH)));
Timeline fadein = new Timeline(new KeyFrame(Duration.millis(320),
new KeyValue(hoursContent.opacityProperty(),
1,
Interpolator.EASE_BOTH)));
new ParallelTransition(fadeout, fadein).play();
} else {
Timeline fadeout = new Timeline(new KeyFrame(Duration.millis(320),
new KeyValue(hoursContent.opacityProperty(),
0,
Interpolator.EASE_BOTH)));
Timeline fadein = new Timeline(new KeyFrame(Duration.millis(320),
new KeyValue(minutesContent.opacityProperty(),
1,
Interpolator.EASE_BOTH)));
new ParallelTransition(fadeout, fadein).play();
}
}
void updateValue() {
if (is24HourView) {
LocalTimeStringConverter localTimeStringConverter =
new LocalTimeStringConverter(FormatStyle.SHORT, Locale.GERMAN);
timePicker.setValue(localTimeStringConverter.fromString(selectedHourLabel.getText()
+ ":" + selectedMinLabel.getText()));
} else {
timePicker.setValue(LocalTime.parse(selectedHourLabel.getText() + ":" + selectedMinLabel.getText() + " " + period.get(), DateTimeFormatter.ofPattern("h:mm a").withLocale(Locale.ENGLISH)));
}
}
private void goToTime(LocalTime time) {
if (time != null) {
int hour = time.getHour();
selectedHourLabel.setText(Integer.toString(hour % (is24HourView ? 24 : 12) == 0 ?
(is24HourView ? 0 : 12) : hour % (is24HourView ? 24 : 12)));
selectedMinLabel.setText(unitConverter.toString(time.getMinute()));
if (!is24HourView) {
period.set(hour < 12 ? "AM" : "PM");
}
minsPointerRotate.setAngle(180 + (time.getMinute() + 45) % 60 * Math.toDegrees(2 * Math.PI / 60));
hoursPointerRotate.setAngle(180 + Math.toDegrees(2 * (hour - 3) * Math.PI / 12));
_24HourHoursPointerRotate.setAngle(180 + Math.toDegrees(2 * (hour - 3) * Math.PI / 12));
}
}
void clearFocus() {
LocalTime focusTime = timePicker.getValue();
if (focusTime == null) {
focusTime = LocalTime.now();
}
goToTime(focusTime);
}
}