├── .gitignore ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── eu │ └── hansolo │ └── fx │ └── timecontrol │ ├── .DS_Store │ ├── Demo.java │ ├── TimeControl.java │ └── fonts │ └── Fonts.java └── resources └── eu └── hansolo └── fx └── timecontrol ├── fonts ├── .DS_Store ├── Roboto-Light.ttf └── Roboto-Regular.ttf └── timecontrol.css /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.gradle 3 | /.idea 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## TimeControl 2 | A little JavaFX component that was inspired by Apple's sleep timer in iOS 10. 3 | 4 | ![TimeControl](https://dl.dropboxusercontent.com/u/84552/TimeControl.png) 5 | 6 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | group 'eu.hansolo.fx' 2 | version '1.0-SNAPSHOT' 3 | 4 | apply plugin: 'java' 5 | 6 | sourceCompatibility = 1.8 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/timecontrol/12dd253709e6b23c02caefc1f2cfd1a5ba0d75c5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Oct 13 15:04:17 CEST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'timecontrol' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/timecontrol/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/timecontrol/12dd253709e6b23c02caefc1f2cfd1a5ba0d75c5/src/main/java/eu/hansolo/fx/timecontrol/.DS_Store -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/timecontrol/Demo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Gerrit Grunwald 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package eu.hansolo.fx.timecontrol; 18 | 19 | import javafx.application.Application; 20 | import javafx.geometry.Insets; 21 | import javafx.scene.layout.Background; 22 | import javafx.scene.layout.BackgroundFill; 23 | import javafx.scene.layout.CornerRadii; 24 | import javafx.scene.paint.Color; 25 | import javafx.stage.Stage; 26 | import javafx.scene.layout.StackPane; 27 | import javafx.scene.Scene; 28 | 29 | import java.time.LocalTime; 30 | 31 | 32 | /** 33 | * User: hansolo 34 | * Date: 13.10.16 35 | * Time: 16:09 36 | */ 37 | public class Demo extends Application { 38 | private TimeControl timeControl; 39 | 40 | @Override public void init() { 41 | timeControl = new TimeControl(); 42 | timeControl.setBarColor(Color.web("#28e80c")); 43 | timeControl.setStartTime(LocalTime.of(0, 0)); 44 | timeControl.setStopTime(LocalTime.of(15, 0)); 45 | 46 | //timeControl.durationProperty().addListener(o -> System.out.println(timeControl.getDuration())); 47 | } 48 | 49 | @Override public void start(Stage stage) { 50 | StackPane pane = new StackPane(timeControl); 51 | pane.setPadding(new Insets(10)); 52 | pane.setBackground(new Background(new BackgroundFill(Color.web("#0d0d0d"), CornerRadii.EMPTY, Insets.EMPTY))); 53 | 54 | Scene scene = new Scene(pane); 55 | 56 | stage.setTitle("TimeControl"); 57 | stage.setScene(scene); 58 | stage.show(); 59 | } 60 | 61 | @Override public void stop() { 62 | System.exit(0); 63 | } 64 | 65 | public static void main(String[] args) { 66 | launch(args); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/timecontrol/TimeControl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Gerrit Grunwald 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package eu.hansolo.fx.timecontrol; 18 | 19 | import eu.hansolo.fx.timecontrol.fonts.Fonts; 20 | import javafx.beans.DefaultProperty; 21 | import javafx.beans.property.ObjectProperty; 22 | import javafx.beans.property.ObjectPropertyBase; 23 | import javafx.beans.property.ReadOnlyObjectProperty; 24 | import javafx.collections.ObservableList; 25 | import javafx.geometry.Point2D; 26 | import javafx.geometry.Pos; 27 | import javafx.geometry.VPos; 28 | import javafx.scene.Node; 29 | import javafx.geometry.Insets; 30 | import javafx.scene.canvas.Canvas; 31 | import javafx.scene.canvas.GraphicsContext; 32 | import javafx.scene.layout.Background; 33 | import javafx.scene.layout.BackgroundFill; 34 | import javafx.scene.layout.Border; 35 | import javafx.scene.layout.BorderStroke; 36 | import javafx.scene.layout.BorderStrokeStyle; 37 | import javafx.scene.layout.BorderWidths; 38 | import javafx.scene.layout.CornerRadii; 39 | import javafx.scene.layout.HBox; 40 | import javafx.scene.layout.Pane; 41 | import javafx.scene.layout.Region; 42 | import javafx.scene.paint.Color; 43 | import javafx.scene.paint.Paint; 44 | import javafx.scene.paint.Stop; 45 | import javafx.scene.shape.Arc; 46 | import javafx.scene.shape.ArcType; 47 | import javafx.scene.shape.Circle; 48 | import javafx.scene.shape.Rectangle; 49 | import javafx.scene.shape.SVGPath; 50 | import javafx.scene.shape.StrokeLineCap; 51 | import javafx.scene.text.Font; 52 | import javafx.scene.text.Text; 53 | import javafx.scene.text.TextAlignment; 54 | import javafx.scene.transform.Rotate; 55 | 56 | import java.sql.Time; 57 | import java.time.Duration; 58 | import java.time.LocalTime; 59 | import java.time.format.DateTimeFormatter; 60 | import java.util.Locale; 61 | 62 | 63 | /** 64 | * User: hansolo 65 | * Date: 13.10.16 66 | * Time: 15:04 67 | */ 68 | @DefaultProperty("children") 69 | public class TimeControl extends Region { 70 | private enum TickLabelOrientation { ORTHOGONAL, HORIZONTAL, TANGENT } 71 | private enum TouchPointType { START, STOP } 72 | private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm"); 73 | private static final DateTimeFormatter HOUR_FORMAT = DateTimeFormatter.ofPattern("HH"); 74 | private static final DateTimeFormatter MINUTE_FORMAT = DateTimeFormatter.ofPattern("mm"); 75 | private static final double ANGLE_STEP = 0.00416667; // degrees per seconds a day 76 | private static final double PREFERRED_WIDTH = 400; 77 | private static final double PREFERRED_HEIGHT = 505; 78 | private static final double MINIMUM_WIDTH = 40; 79 | private static final double MINIMUM_HEIGHT = 50; 80 | private static final double MAXIMUM_WIDTH = 1024; 81 | private static final double MAXIMUM_HEIGHT = 1024; 82 | private static final double ASPECT_RATIO = PREFERRED_HEIGHT / PREFERRED_WIDTH; 83 | private double size; 84 | private double width; 85 | private double height; 86 | private Text startText; 87 | private Text startTimeText; 88 | private Text stopText; 89 | private Text stopTimeText; 90 | private Text hourText; 91 | private Text hourUnitText; 92 | private Text minuteText; 93 | private Text minuteUnitText; 94 | private HBox durationBox; 95 | private Canvas canvas; 96 | private Arc barBackground; 97 | private Arc bar; 98 | private Rotate barRotate; 99 | private Circle touchPointStart; 100 | private Circle touchPointStop; 101 | private Rotate touchRotate; 102 | private double iconSize; 103 | private Region startIcon; 104 | private Region stopIcon; 105 | private Region startPointIcon; 106 | private Region stopPointIcon; 107 | private GraphicsContext ctx; 108 | private Pane pane; 109 | private double mouseScaleX; 110 | private double mouseScaleY; 111 | private ObjectProperty barBackgroundColor; 112 | private ObjectProperty barColor; 113 | private ObjectProperty backgroundColor; 114 | private ObjectProperty textColor; 115 | private ObjectProperty duration; 116 | private Paint backgroundPaint; 117 | private Paint borderPaint; 118 | private double borderWidth; 119 | private ObjectProperty startTime; 120 | private ObjectProperty stopTime; 121 | 122 | 123 | 124 | // ******************** Constructors ************************************** 125 | public TimeControl() { 126 | getStylesheets().add(TimeControl.class.getResource("timecontrol.css").toExternalForm()); 127 | barBackgroundColor = new ObjectPropertyBase(Color.web("#171717")) { 128 | @Override protected void invalidated() { resize(); } 129 | @Override public Object getBean() { return TimeControl.this; } 130 | @Override public String getName() { return "barBackgroundColor"; } 131 | }; 132 | barColor = new ObjectPropertyBase(Color.web("#ffb500")) { 133 | @Override protected void invalidated() { resize(); } 134 | @Override public Object getBean() { return TimeControl.this; } 135 | @Override public String getName() { return "barColor"; } 136 | }; 137 | backgroundColor = new ObjectPropertyBase(Color.web("#0d0d0d")) { 138 | @Override protected void invalidated() { backgroundPaint = get(); redraw(); } 139 | @Override public Object getBean() { return TimeControl.this; } 140 | @Override public String getName() { return "backgroundColor"; } 141 | }; 142 | textColor = new ObjectPropertyBase(Color.WHITE) { 143 | @Override protected void invalidated() { resize(); } 144 | @Override public Object getBean() { return TimeControl.this; } 145 | @Override public String getName() { return "textColor"; } 146 | }; 147 | iconSize = 40; 148 | backgroundPaint = Color.web("#0d0d0d"); 149 | borderPaint = Color.TRANSPARENT; 150 | borderWidth = 0d; 151 | mouseScaleX = 1.0; 152 | mouseScaleY = 1.0; 153 | startTime = new ObjectPropertyBase(LocalTime.of(0, 0)) { 154 | @Override protected void invalidated() { updateBar(); } 155 | @Override public Object getBean() { return TimeControl.this; } 156 | @Override public String getName() { return "startTime"; } 157 | }; 158 | stopTime = new ObjectPropertyBase(LocalTime.of(0, 0)) { 159 | @Override protected void invalidated() { updateBar(); } 160 | @Override public Object getBean() { return TimeControl.this; } 161 | @Override public String getName() { return "stopTime"; } 162 | }; 163 | duration = new ObjectPropertyBase(Duration.between(getStopTime(), getStartTime())) { 164 | @Override public Object getBean() { return TimeControl.this; } 165 | @Override public String getName() { return "duration"; } 166 | }; 167 | 168 | initGraphics(); 169 | registerListeners(); 170 | } 171 | 172 | 173 | // ******************** Initialization ************************************ 174 | private void initGraphics() { 175 | if (Double.compare(getPrefWidth(), 0.0) <= 0 || Double.compare(getPrefHeight(), 0.0) <= 0 || 176 | Double.compare(getWidth(), 0.0) <= 0 || Double.compare(getHeight(), 0.0) <= 0) { 177 | if (getPrefWidth() > 0 && getPrefHeight() > 0) { 178 | setPrefSize(getPrefWidth(), getPrefHeight()); 179 | } else { 180 | setPrefSize(PREFERRED_WIDTH, PREFERRED_HEIGHT); 181 | } 182 | } 183 | 184 | getStyleClass().add("time-control"); 185 | 186 | startIcon = new Region(); 187 | startIcon.getStyleClass().add("start-icon"); 188 | 189 | stopIcon = new Region(); 190 | stopIcon.getStyleClass().add("stop-icon"); 191 | 192 | startText = new Text("Start"); 193 | startText.setTextOrigin(VPos.TOP); 194 | 195 | startTimeText = new Text("00:00"); 196 | startTimeText.setTextOrigin(VPos.TOP); 197 | 198 | stopText = new Text("Stop"); 199 | stopText.setTextOrigin(VPos.TOP); 200 | 201 | stopTimeText = new Text("00:00"); 202 | stopTimeText.setTextOrigin(VPos.TOP); 203 | 204 | hourText = new Text("0"); 205 | hourText.setTextOrigin(VPos.CENTER); 206 | 207 | hourUnitText = new Text("h"); 208 | hourUnitText.setTextOrigin(VPos.BOTTOM); 209 | 210 | minuteText = new Text("0"); 211 | minuteText.setTextOrigin(VPos.CENTER); 212 | 213 | minuteUnitText = new Text("m"); 214 | minuteUnitText.setTextOrigin(VPos.BOTTOM); 215 | 216 | durationBox = new HBox(hourText, hourUnitText, minuteText, minuteUnitText); 217 | durationBox.setAlignment(Pos.BASELINE_CENTER); 218 | 219 | canvas = new Canvas(400, 400); 220 | ctx = canvas.getGraphicsContext2D(); 221 | 222 | barBackground = new Arc(200, 350, 200, 200, 0, 360); 223 | barBackground.setStrokeLineCap(StrokeLineCap.BUTT); 224 | barBackground.setType(ArcType.OPEN); 225 | barBackground.setFill(null); 226 | 227 | barRotate = new Rotate(-90, PREFERRED_WIDTH * 0.5, PREFERRED_HEIGHT * 0.6039604); 228 | 229 | bar = new Arc(200, 350, 200, 200, 0, 0); 230 | bar.setStrokeLineCap(StrokeLineCap.ROUND); 231 | bar.setType(ArcType.OPEN); 232 | bar.setFill(null); 233 | bar.getTransforms().add(barRotate); 234 | 235 | touchRotate = new Rotate(-180, PREFERRED_WIDTH * 0.5, PREFERRED_HEIGHT * 0.6039604); 236 | 237 | touchPointStart = new Circle(); 238 | touchPointStart.getTransforms().add(touchRotate); 239 | 240 | touchPointStop = new Circle(); 241 | touchPointStop.getTransforms().add(touchRotate); 242 | 243 | startPointIcon = new Region(); 244 | startPointIcon.getStyleClass().add("start-icon"); 245 | startPointIcon.setMouseTransparent(true); 246 | 247 | stopPointIcon = new Region(); 248 | stopPointIcon.getStyleClass().add("stop-icon"); 249 | stopPointIcon.setMouseTransparent(true); 250 | 251 | pane = new Pane(startIcon, startText, startTimeText, stopIcon, stopText, stopTimeText, durationBox, canvas, barBackground, bar, touchPointStart, touchPointStop, startPointIcon, stopPointIcon); 252 | pane.setBackground(new Background(new BackgroundFill(backgroundPaint, CornerRadii.EMPTY, Insets.EMPTY))); 253 | pane.setBorder(new Border(new BorderStroke(borderPaint, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(borderWidth)))); 254 | 255 | getChildren().setAll(pane); 256 | } 257 | 258 | private void registerListeners() { 259 | widthProperty().addListener(o -> resize()); 260 | heightProperty().addListener(o -> resize()); 261 | touchPointStart.setOnMouseDragged(evt -> touchRotate(evt.getSceneX() * mouseScaleX, evt.getSceneY() * mouseScaleY, TouchPointType.START)); 262 | touchPointStop.setOnMouseDragged(evt -> touchRotate(evt.getSceneX() * mouseScaleX, evt.getSceneY() * mouseScaleY, TouchPointType.STOP)); 263 | } 264 | 265 | 266 | // ******************** Methods ******************************************* 267 | @Override protected double computeMinWidth(final double HEIGHT) { return MINIMUM_WIDTH; } 268 | @Override protected double computeMinHeight(final double WIDTH) { return MINIMUM_HEIGHT; } 269 | @Override protected double computePrefWidth(final double HEIGHT) { return super.computePrefWidth(HEIGHT); } 270 | @Override protected double computePrefHeight(final double WIDTH) { return super.computePrefHeight(WIDTH); } 271 | @Override protected double computeMaxWidth(final double HEIGHT) { return MAXIMUM_WIDTH; } 272 | @Override protected double computeMaxHeight(final double WIDTH) { return MAXIMUM_HEIGHT; } 273 | 274 | @Override public ObservableList getChildren() { return super.getChildren(); } 275 | 276 | public Color getBackgroundColor() { return backgroundColor.get(); } 277 | public void setBackgroundColor(final Color COLOR) { backgroundColor.set(COLOR); } 278 | public ObjectProperty backgroundColorProperty() { return backgroundColor; } 279 | 280 | public Color getBarBackgroundColor() { return barBackgroundColor.get(); } 281 | public void setBarBackgroundColor(final Color COLOR) { barBackgroundColor.set(COLOR); } 282 | public ObjectProperty barBackgroundColorProperty() { return barBackgroundColor; } 283 | 284 | public Color getBarColor() { return barColor.get(); } 285 | public void setBarColor(final Color COLOR) { barColor.set(COLOR); } 286 | public ObjectProperty barColorProperty() { return barColor; } 287 | 288 | public Color getTextColor() { return textColor.get(); } 289 | public void setTextColor(final Color COLOR) { textColor.set(COLOR); } 290 | public ObjectProperty textColorProperty() { return textColor; } 291 | 292 | public LocalTime getStartTime() { return startTime.get(); } 293 | public void setStartTime(final LocalTime TIME) { startTime.set(TIME); } 294 | public ObjectProperty startTimeProperty() { return startTime; } 295 | 296 | public LocalTime getStopTime() { return stopTime.get(); } 297 | public void setStopTime(final LocalTime TIME) { stopTime.set(TIME); } 298 | public ObjectProperty stopTimeProperty() { return stopTime; } 299 | 300 | public Duration getDuration() { return duration.get(); } 301 | public ReadOnlyObjectProperty durationProperty() { return duration; } 302 | 303 | 304 | private void drawTickmarks() { 305 | double sinValue; 306 | double cosValue; 307 | double ctxSize = canvas.getWidth(); 308 | double startAngle = 180; 309 | double angleStep = 3.75; 310 | Point2D center = new Point2D(ctxSize * 0.5, ctxSize * 0.5); 311 | Color minorTickMarkColor = Color.color(getTextColor().getRed(), getTextColor().getGreen(), getTextColor().getBlue(), 0.25); 312 | Color majorTickMarkColor = Color.color(getTextColor().getRed(), getTextColor().getGreen(), getTextColor().getBlue(), 0.5); 313 | Font font = Fonts.robotoLight(width * 0.04); 314 | ctx.clearRect(0, 0, ctxSize, ctxSize); 315 | ctx.setLineCap(StrokeLineCap.BUTT); 316 | ctx.setFont(font); 317 | ctx.setLineWidth(ctxSize * 0.005); 318 | for (double angle = 0, counter = 0 ; Double.compare(counter, 95) <= 0 ; angle -= angleStep, counter++) { 319 | sinValue = Math.sin(Math.toRadians(angle + startAngle)); 320 | cosValue = Math.cos(Math.toRadians(angle + startAngle)); 321 | 322 | Point2D innerPoint = new Point2D(center.getX() + ctxSize * 0.465 * sinValue, center.getY() + ctxSize * 0.465 * cosValue); 323 | Point2D innerMinutePoint = new Point2D(center.getX() + ctxSize * 0.478 * sinValue, center.getY() + ctxSize * 0.478 * cosValue); 324 | Point2D outerPoint = new Point2D(center.getX() + ctxSize * 0.5 * sinValue, center.getY() + ctxSize * 0.5 * cosValue); 325 | Point2D textPoint = new Point2D(center.getX() + ctxSize * 0.42 * sinValue, center.getY() + ctxSize * 0.42 * cosValue); 326 | 327 | if (counter % 4 == 0) { 328 | ctx.setStroke(majorTickMarkColor); 329 | ctx.strokeLine(innerPoint.getX(), innerPoint.getY(), outerPoint.getX(), outerPoint.getY()); 330 | ctx.save(); 331 | ctx.translate(textPoint.getX(), textPoint.getY()); 332 | 333 | rotateContextForText(ctx, startAngle, angle, TickLabelOrientation.HORIZONTAL); 334 | ctx.setTextAlign(TextAlignment.CENTER); 335 | ctx.setTextBaseline(VPos.CENTER); 336 | ctx.setFill(majorTickMarkColor); 337 | ctx.fillText(counter == 0 ? "0" : Integer.toString((int) (counter / 4)), 0, 0); 338 | ctx.restore(); 339 | } else if (counter % 1 == 0) { 340 | ctx.setStroke(minorTickMarkColor); 341 | ctx.strokeLine(innerMinutePoint.getX(), innerMinutePoint.getY(), outerPoint.getX(), outerPoint.getY()); 342 | } 343 | } 344 | } 345 | 346 | private void rotateContextForText(final GraphicsContext CTX, final double START_ANGLE, final double ANGLE, final TickLabelOrientation ORIENTATION) { 347 | switch (ORIENTATION) { 348 | case ORTHOGONAL: 349 | if ((360 - START_ANGLE - ANGLE) % 360 > 90 && (360 - START_ANGLE - ANGLE) % 360 < 270) { 350 | CTX.rotate((180 - START_ANGLE - ANGLE) % 360); 351 | } else { 352 | CTX.rotate((360 - START_ANGLE - ANGLE) % 360); 353 | } 354 | break; 355 | case TANGENT: 356 | if ((360 - START_ANGLE - ANGLE - 90) % 360 > 90 && (360 - START_ANGLE - ANGLE - 90) % 360 < 270) { 357 | CTX.rotate((90 - START_ANGLE - ANGLE) % 360); 358 | } else { 359 | CTX.rotate((270 - START_ANGLE - ANGLE) % 360); 360 | } 361 | break; 362 | case HORIZONTAL: 363 | default: 364 | break; 365 | } 366 | } 367 | 368 | private void touchRotate(final double X, final double Y, final TouchPointType TYPE) { 369 | double theta = getTheta(X, Y); 370 | double angle = -theta % 360; 371 | double touchPointAngle = Math.toRadians(-theta - 90); 372 | if (TouchPointType.START == TYPE) { 373 | touchPointStart.setCenterX(bar.getCenterX() + bar.getRadiusX() * Math.sin(touchPointAngle)); 374 | touchPointStart.setCenterY(bar.getCenterY() + bar.getRadiusY() * Math.cos(touchPointAngle)); 375 | startPointIcon.resize(iconSize, iconSize); 376 | startPointIcon.relocate(touchPointStart.getBoundsInParent().getMinX() + iconSize * 0.6, touchPointStart.getBoundsInParent().getMinY() + iconSize * 0.6); 377 | setStartTime(LocalTime.ofSecondOfDay((long) Math.abs(angle / ANGLE_STEP)).plusSeconds(21600)); 378 | } else { 379 | touchPointStop.setCenterX(bar.getCenterX() + bar.getRadiusX() * Math.sin(touchPointAngle)); 380 | touchPointStop.setCenterY(bar.getCenterY() + bar.getRadiusY() * Math.cos(touchPointAngle)); 381 | stopPointIcon.resize(iconSize, iconSize); 382 | stopPointIcon.relocate(touchPointStop.getBoundsInParent().getMinX() + iconSize * 0.6, touchPointStop.getBoundsInParent().getMinY() + iconSize * 0.6); 383 | setStopTime(LocalTime.ofSecondOfDay((long) Math.abs(angle / ANGLE_STEP)).plusSeconds(21600)); 384 | } 385 | } 386 | 387 | private double getTheta(double x, double y) { 388 | double deltaX = x - (width * 0.5); 389 | double deltaY = y - (height * 0.6039604); 390 | double radius = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY)); 391 | double nx = deltaX / radius; 392 | double ny = deltaY / radius; 393 | double theta = Math.atan2(ny, nx); 394 | return Double.compare(theta, 0.0) >= 0 ? Math.toDegrees(theta) : Math.toDegrees((theta)) + 360.0; 395 | } 396 | 397 | private void updateBar() { 398 | int stopPointSeconds = getStopTime().toSecondOfDay(); 399 | int startPointSeconds = getStartTime().toSecondOfDay() > stopPointSeconds ? getStartTime().toSecondOfDay() - 86400 : getStartTime().toSecondOfDay(); 400 | int deltaSeconds = Math.abs(stopPointSeconds - startPointSeconds); 401 | duration.set(Duration.ofSeconds(deltaSeconds)); 402 | 403 | bar.setStartAngle(-startPointSeconds * ANGLE_STEP); 404 | bar.setLength(-deltaSeconds * ANGLE_STEP); 405 | 406 | startTimeText.setText(TIME_FORMAT.format(getStartTime())); 407 | stopTimeText.setText(TIME_FORMAT.format(getStopTime())); 408 | 409 | hourText.setText(HOUR_FORMAT.format((getStopTime().minusSeconds(startPointSeconds)))); 410 | minuteText.setText(MINUTE_FORMAT.format((getStopTime().minusSeconds(startPointSeconds)))); 411 | durationBox.setLayoutX((width - durationBox.getLayoutBounds().getWidth()) * 0.5); 412 | } 413 | 414 | 415 | // ******************** Resizing ****************************************** 416 | private void resize() { 417 | width = getWidth() - getInsets().getLeft() - getInsets().getRight(); 418 | height = getHeight() - getInsets().getTop() - getInsets().getBottom(); 419 | size = width < height ? width : height; 420 | 421 | if (ASPECT_RATIO * width > height) { 422 | width = 1 / (ASPECT_RATIO / height); 423 | } else if (1 / (ASPECT_RATIO / height) > width) { 424 | height = ASPECT_RATIO * width; 425 | } 426 | 427 | if (width > 0 && height > 0) { 428 | pane.setMaxSize(width, height); 429 | pane.setPrefSize(width, height); 430 | pane.relocate((getWidth() - width) * 0.5, (getHeight() - height) * 0.5); 431 | 432 | if (Double.compare(pane.getLayoutBounds().getWidth(), 0) == 0) { 433 | mouseScaleX = 1.0; 434 | mouseScaleY = 1.0; 435 | } else { 436 | mouseScaleX = pane.getLayoutBounds().getWidth() / getWidth(); 437 | mouseScaleY = pane.getLayoutBounds().getHeight() / getHeight(); 438 | } 439 | 440 | iconSize = width * 0.04725; 441 | 442 | startIcon.setPrefSize(iconSize, iconSize); 443 | startIcon.relocate(0, width * 0.022); 444 | 445 | startText.setFont(Fonts.robotoRegular(width * 0.05)); 446 | startText.setX(width * 0.065); 447 | startText.setY(width * 0.0125); 448 | 449 | startTimeText.setFont(Fonts.robotoRegular(width * 0.12)); 450 | startTimeText.setX(width * 0.05); 451 | startTimeText.setY(width * 0.0825); 452 | 453 | stopText.setFont(Fonts.robotoRegular(width * 0.05)); 454 | stopText.setX(width - stopText.getLayoutBounds().getWidth()); 455 | stopText.setY(width * 0.0125); 456 | 457 | stopIcon.setPrefSize(iconSize, iconSize); 458 | stopIcon.relocate((width - stopText.getLayoutBounds().getWidth()) - width * 0.065, width * 0.022); 459 | 460 | stopTimeText.setFont(Fonts.robotoRegular(width * 0.12)); 461 | stopTimeText.setX((width - stopTimeText.getLayoutBounds().getWidth()) - width * 0.05); 462 | stopTimeText.setY(width * 0.0825); 463 | 464 | hourText.setFont(Fonts.robotoRegular(width * 0.11)); 465 | hourUnitText.setFont(Fonts.robotoRegular(width * 0.05)); 466 | minuteText.setFont(Fonts.robotoRegular(width * 0.11)); 467 | minuteUnitText.setFont(Fonts.robotoRegular(width * 0.05)); 468 | 469 | if (Double.compare(durationBox.getLayoutBounds().getWidth(), 0) == 0) { 470 | durationBox.setLayoutX(width * 0.306); 471 | durationBox.setLayoutY(height * 0.54); 472 | } else { 473 | durationBox.relocate((width - durationBox.getLayoutBounds().getWidth()) * 0.5, height * 0.54); 474 | } 475 | durationBox.setSpacing(width * 0.0125); 476 | HBox.setMargin(hourUnitText, new Insets(0, width * 0.0125, 0, 0)); 477 | HBox.setMargin(minuteText, new Insets(0, 0, 0, width * 0.0125)); 478 | 479 | canvas.setWidth(width * 0.75); 480 | canvas.setHeight(width * 0.75); 481 | drawTickmarks(); 482 | canvas.relocate((width - canvas.getWidth()) * 0.5, height * 0.30693069); 483 | 484 | barBackground.setStrokeWidth(width * 0.115); 485 | barBackground.setCenterX(width * 0.5); 486 | barBackground.setCenterY(height * 0.6039604); 487 | barBackground.setRadiusX(width * 0.4425); 488 | barBackground.setRadiusY(width * 0.4425); 489 | 490 | barBackground.setStrokeWidth(width * 0.115); 491 | barBackground.setCenterX(width * 0.5); 492 | barBackground.setCenterY(height * 0.6039604); 493 | barBackground.setRadiusX(width * 0.4425); 494 | barBackground.setRadiusY(width * 0.4425); 495 | 496 | barRotate.setPivotX(width * 0.5); 497 | barRotate.setPivotY(height * 0.6039604); 498 | 499 | bar.setStrokeWidth(width * 0.115); 500 | bar.setCenterX(width * 0.5); 501 | bar.setCenterY(height * 0.6039604); 502 | bar.setRadiusX(width * 0.4425); 503 | bar.setRadiusY(width * 0.4425); 504 | 505 | touchRotate.setPivotX(width * 0.5); 506 | touchRotate.setPivotY(height * 0.6039604); 507 | 508 | touchPointStart.setCenterX(bar.getCenterX() + bar.getRadiusX() * Math.sin(Math.toRadians(-getStartTime().toSecondOfDay() * ANGLE_STEP))); 509 | touchPointStart.setCenterY(bar.getCenterY() + bar.getRadiusY() * Math.cos(Math.toRadians(-getStartTime().toSecondOfDay() * ANGLE_STEP))); 510 | touchPointStart.setRadius(width * 0.0525); 511 | 512 | touchPointStop.setCenterX(bar.getCenterX() + bar.getRadiusX() * Math.sin(Math.toRadians(-getStopTime().toSecondOfDay() * ANGLE_STEP))); 513 | touchPointStop.setCenterY(bar.getCenterY() + bar.getRadiusY() * Math.cos(Math.toRadians(-getStopTime().toSecondOfDay() * ANGLE_STEP))); 514 | touchPointStop.setRadius(width * 0.0525); 515 | 516 | startPointIcon.setPrefSize(iconSize, iconSize); 517 | startPointIcon.relocate(touchPointStart.getBoundsInParent().getMinX() + iconSize * 0.6, touchPointStart.getBoundsInParent().getMinY() + iconSize * 0.6); 518 | 519 | stopPointIcon.setPrefSize(iconSize, iconSize); 520 | stopPointIcon.relocate(touchPointStop.getBoundsInParent().getMinX() + iconSize * 0.6, touchPointStop.getBoundsInParent().getMinY() + iconSize * 0.6); 521 | 522 | redraw(); 523 | } 524 | } 525 | 526 | private void redraw() { 527 | startIcon.setBackground(new Background(new BackgroundFill(getBarColor(), CornerRadii.EMPTY, Insets.EMPTY))); 528 | stopIcon.setBackground(new Background(new BackgroundFill(getBarColor(), CornerRadii.EMPTY, Insets.EMPTY))); 529 | 530 | startText.setFill(getTextColor()); 531 | startTimeText.setFill(getTextColor()); 532 | 533 | stopText.setFill(getTextColor()); 534 | stopTimeText.setFill(getTextColor()); 535 | 536 | hourText.setFill(getTextColor()); 537 | hourUnitText.setFill(getTextColor()); 538 | minuteText.setFill(getTextColor()); 539 | minuteUnitText.setFill(getTextColor()); 540 | 541 | barBackground.setStroke(getBarBackgroundColor()); 542 | bar.setStroke(getBarColor()); 543 | 544 | touchPointStart.setFill(getBarBackgroundColor()); 545 | touchPointStop.setFill(getBarBackgroundColor()); 546 | 547 | startPointIcon.setBackground(new Background(new BackgroundFill(getBarColor(), CornerRadii.EMPTY, Insets.EMPTY))); 548 | stopPointIcon.setBackground(new Background(new BackgroundFill(getBarColor(), CornerRadii.EMPTY, Insets.EMPTY))); 549 | 550 | pane.setBackground(new Background(new BackgroundFill(backgroundPaint, CornerRadii.EMPTY, Insets.EMPTY))); 551 | pane.setBorder(new Border(new BorderStroke(borderPaint, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(borderWidth / PREFERRED_WIDTH * size)))); 552 | } 553 | } 554 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/timecontrol/fonts/Fonts.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Gerrit Grunwald 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package eu.hansolo.fx.timecontrol.fonts; 18 | 19 | import javafx.scene.text.Font; 20 | 21 | 22 | /** 23 | * User: hansolo 24 | * Date: 01.10.16 25 | * Time: 04:47 26 | */ 27 | public class Fonts { 28 | private static final String ROBOTO_LIGHT_NAME; 29 | private static final String ROBOTO_REGULAR_NAME; 30 | 31 | private static String robotoLightName; 32 | private static String robotoRegularName; 33 | 34 | 35 | static { 36 | try { 37 | robotoLightName = Font.loadFont(Fonts.class.getResourceAsStream("/eu/hansolo/fx/timecontrol/fonts/Roboto-Light.ttf"), 10).getName(); 38 | robotoRegularName = Font.loadFont(Fonts.class.getResourceAsStream("/eu/hansolo/fx/timecontrol/fonts/Roboto-Regular.ttf"), 10).getName(); 39 | } catch (Exception exception) { } 40 | ROBOTO_LIGHT_NAME = robotoLightName; 41 | ROBOTO_REGULAR_NAME = robotoRegularName; 42 | } 43 | 44 | 45 | // ******************** Methods ******************************************* 46 | public static Font robotoLight(final double SIZE) { return new Font(ROBOTO_LIGHT_NAME, SIZE); } 47 | public static Font robotoRegular(final double SIZE) { return new Font(ROBOTO_REGULAR_NAME, SIZE); } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/timecontrol/fonts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/timecontrol/12dd253709e6b23c02caefc1f2cfd1a5ba0d75c5/src/main/resources/eu/hansolo/fx/timecontrol/fonts/.DS_Store -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/timecontrol/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/timecontrol/12dd253709e6b23c02caefc1f2cfd1a5ba0d75c5/src/main/resources/eu/hansolo/fx/timecontrol/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/timecontrol/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/timecontrol/12dd253709e6b23c02caefc1f2cfd1a5ba0d75c5/src/main/resources/eu/hansolo/fx/timecontrol/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/timecontrol/timecontrol.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Gerrit Grunwald 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | .time-control { 17 | 18 | } 19 | 20 | .start-icon { 21 | -fx-scale-shape: true; 22 | -fx-shape : "M 5 5 L 35 20 L 5 35 L 5 5 Z"; 23 | } 24 | 25 | .stop-icon { 26 | -fx-scale-shape: true; 27 | -fx-shape : "M 5 5 L 35 5 L 35 35 L 5 35 L 5 5 Z"; 28 | } 29 | --------------------------------------------------------------------------------