001/* 002 * ============================================================================ 003 * Copyright © 2002-2024 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * Licensed to the public under the agreements of the GNU Lesser General Public 007 * License, version 3.0 (the "License"). You may obtain a copy of the License at 008 * 009 * http://www.gnu.org/licenses/lgpl.html 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations 015 * under the License. 016 */ 017 018package org.tquadrat.foundation.fx.control.tester; 019 020import static java.lang.String.format; 021import static java.lang.System.err; 022import static java.lang.System.out; 023import static javafx.beans.binding.Bindings.createStringBinding; 024import static javafx.scene.layout.Region.USE_COMPUTED_SIZE; 025import static org.apiguardian.api.API.Status.EXPERIMENTAL; 026 027import java.time.LocalDate; 028import java.time.LocalTime; 029import java.time.ZoneId; 030import java.time.ZonedDateTime; 031import java.time.format.DateTimeFormatterBuilder; 032import java.time.temporal.ChronoField; 033 034import org.apiguardian.api.API; 035import org.tquadrat.foundation.annotation.ClassVersion; 036import org.tquadrat.foundation.annotation.ProgramClass; 037import org.tquadrat.foundation.fx.control.RangeSlider; 038import javafx.application.Application; 039import javafx.geometry.Insets; 040import javafx.scene.Scene; 041import javafx.scene.control.TextField; 042import javafx.scene.layout.HBox; 043import javafx.scene.layout.Pane; 044import javafx.scene.layout.VBox; 045import javafx.stage.Stage; 046 047/** 048 * Test bed for the custom control 049 * {@link RangeSlider}. 050 * 051 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 052 * @version $Id: RangeSliderTester.java 1113 2024-03-12 02:01:14Z tquadrat $ 053 * @since 0.4.6 054 * 055 * @UMLGraph.link 056 */ 057@SuppressWarnings( "UseOfSystemOutOrSystemErr" ) 058@ClassVersion( sourceVersion = "$Id: RangeSliderTester.java 1113 2024-03-12 02:01:14Z tquadrat $" ) 059@API( status = EXPERIMENTAL, since = "0.4.6" ) 060@ProgramClass 061public final class RangeSliderTester extends Application 062{ 063 /*---------------*\ 064 ====** Inner Classes **==================================================== 065 \*---------------*/ 066 067 /*-----------*\ 068 ====** Constants **======================================================== 069 \*-----------*/ 070 071 /*------------*\ 072 ====** Attributes **======================================================= 073 \*------------*/ 074 /** 075 * The spacing. 076 */ 077 private final double m_Spacing = 7.5; 078 079 /*------------------------*\ 080 ====** Static Initialisations **=========================================== 081 \*------------------------*/ 082 /** 083 * The flag that tracks the assertion on/off status for this package. 084 */ 085 private static boolean m_AssertionOn; 086 087 static 088 { 089 //---* Determine the assertion status *-------------------------------- 090 m_AssertionOn = false; 091 //noinspection AssertWithSideEffects,PointlessBooleanExpression,NestedAssignment 092 assert (m_AssertionOn = true) == true : "Assertion is switched off"; 093 } 094 095 /*--------------*\ 096 ====** Constructors **===================================================== 097 \*--------------*/ 098 /** 099 * Creates a new instance of {@code RangeSliderTester}. 100 */ 101 public RangeSliderTester() { /* Just exists */ } 102 103 /*---------*\ 104 ====** Methods **========================================================== 105 \*---------*/ 106 /** 107 * Creates a simple 108 * {@link RangeSlider}. 109 * 110 * @param root The parent node. 111 */ 112 private final void createSimpleRangeSlider( final Pane root ) 113 { 114 final var paramBox = new HBox(); 115 paramBox.setSpacing( m_Spacing ); 116 paramBox.setPadding( new Insets( m_Spacing ) ); 117 root.getChildren().add( paramBox ); 118 final var min = new TextField(); 119 final var max = new TextField(); 120 paramBox.getChildren().addAll( min, max ); 121 122 final var sliderBox = new HBox(); 123 sliderBox.setSpacing( m_Spacing ); 124 sliderBox.setPadding( new Insets( m_Spacing ) ); 125 root.getChildren().add( sliderBox ); 126 127 final var rangeSlider = new RangeSlider( 0.0, 100.0, 25.0, 75.0 ); 128 rangeSlider.setMinWidth( 600.0 ); 129 rangeSlider.setPrefWidth( USE_COMPUTED_SIZE ); 130 rangeSlider.setMaxWidth( Double.MAX_VALUE ); 131 rangeSlider.setMajorTickUnit( 10.0 ); 132 rangeSlider.setMinorTickCount( 9 ); 133 rangeSlider.setSnapToTicks( true ); 134 rangeSlider.setShowTickLabels( true ); 135 rangeSlider.setShowTickMarks( true ); 136 sliderBox.getChildren().add( rangeSlider ); 137 min.textProperty().set( format( "%.2f", rangeSlider.getMin() ) ); 138 max.textProperty().set( format( "%.2f", rangeSlider.getMax() ) ); 139 140 final var fieldBox = new HBox(); 141 fieldBox.setSpacing( m_Spacing ); 142 fieldBox.setPadding( new Insets( m_Spacing ) ); 143 root.getChildren().add( fieldBox ); 144 145 final var low = new TextField(); 146 final var lowValueBinding = createStringBinding( () -> format( "%.2f", rangeSlider.getLowValue() ), rangeSlider.lowValueProperty() ); 147 low.textProperty().bind( lowValueBinding ); 148 final var range = new TextField(); 149 final var rangeValueBinding = createStringBinding( () -> format( "%.2f", rangeSlider.getHighValue() - rangeSlider.getLowValue() ), rangeSlider.lowValueProperty(), rangeSlider.highValueProperty() ); 150 range.textProperty().bind( rangeValueBinding ); 151 final var high = new TextField(); 152 final var highValueBinding = createStringBinding( () -> format( "%.2f", rangeSlider.getHighValue() ), rangeSlider.highValueProperty() ); 153 high.textProperty().bind( highValueBinding ); 154 fieldBox.getChildren().addAll( low, range, high ); 155 } // createSimpleRangeSlider() 156 157 /** 158 * Creates a simple 159 * {@link RangeSlider}. 160 * 161 * @param root The parent node. 162 */ 163 private final void createTimeRangeSlider( final Pane root ) 164 { 165 final var paramBox = new HBox(); 166 paramBox.setSpacing( m_Spacing ); 167 paramBox.setPadding( new Insets( m_Spacing ) ); 168 root.getChildren().add( paramBox ); 169 final var min = new TextField(); 170 final var max = new TextField(); 171 paramBox.getChildren().addAll( min, max ); 172 173 final var sliderBox = new HBox(); 174 sliderBox.setSpacing( m_Spacing ); 175 sliderBox.setPadding( new Insets( m_Spacing ) ); 176 root.getChildren().add( sliderBox ); 177 178 final var day = LocalDate.now(); 179 final var timeZone = ZoneId.systemDefault(); 180 final var timeFormatter = new DateTimeFormatterBuilder() 181 .appendValue( ChronoField.HOUR_OF_DAY, 2 ) 182 .appendLiteral( ':' ) 183 .appendValue( ChronoField.MINUTE_OF_HOUR, 2 ) 184 .toFormatter(); 185 186 final var stringConverter = new javafx.util.StringConverter<Number>() 187 { 188 /** 189 * {@inheritDoc} 190 */ 191 @Override 192 public final Number fromString( final String s ) { return 0.0D; } 193 194 /** 195 * {@inheritDoc} 196 */ 197 @Override 198 public final String toString( final Number number ) 199 { 200 final var time = LocalTime.ofSecondOfDay( number.intValue() * 60 ); 201 final var offsetDateTime = ZonedDateTime.of( day, time, timeZone ).toOffsetDateTime(); 202 final var retValue = timeFormatter.format( offsetDateTime ); 203 204 //---* Done *---------------------------------------------------------- 205 return retValue; 206 } // toString() 207 }; 208 209 final var rangeSlider = new RangeSlider( 0.0, 24.0 * 60.0 - 0.01, 8.0 * 60.0, 17.0 * 60.0 ); 210 rangeSlider.setMinWidth( 600.0 ); 211 rangeSlider.setPrefWidth( USE_COMPUTED_SIZE ); 212 rangeSlider.setMaxWidth( Double.MAX_VALUE ); 213 rangeSlider.setMajorTickUnit( 60.0 ); 214 rangeSlider.setMinorTickCount( 3 ); 215 rangeSlider.setSnapToTicks( true ); 216 rangeSlider.setShowTickLabels( true ); 217 rangeSlider.setShowTickMarks( true ); 218 rangeSlider.setLabelFormatter( stringConverter ); 219 sliderBox.getChildren().add( rangeSlider ); 220 min.textProperty().set( format( "%.2f", rangeSlider.getMin() ) ); 221 max.textProperty().set( format( "%.2f", rangeSlider.getMax() ) ); 222 223 final var fieldBox = new HBox(); 224 fieldBox.setSpacing( m_Spacing ); 225 fieldBox.setPadding( new Insets( m_Spacing ) ); 226 root.getChildren().add( fieldBox ); 227 228 final var low = new TextField(); 229 final var lowValueBinding = createStringBinding( () -> stringConverter.toString( rangeSlider.getLowValue() ), rangeSlider.lowValueProperty() ); 230 low.textProperty().bind( lowValueBinding ); 231 final var range = new TextField(); 232 final var rangeValueBinding = createStringBinding( () -> stringConverter.toString( rangeSlider.getHighValue() - rangeSlider.getLowValue() ), rangeSlider.lowValueProperty(), rangeSlider.highValueProperty() ); 233 range.textProperty().bind( rangeValueBinding ); 234 final var high = new TextField(); 235 final var highValueBinding = createStringBinding( () -> stringConverter.toString( rangeSlider.getHighValue() ), rangeSlider.highValueProperty() ); 236 high.textProperty().bind( highValueBinding ); 237 fieldBox.getChildren().addAll( low, range, high ); 238 } // createTimeRangeSlider() 239 240 /** 241 * The program entry point. 242 * 243 * @param args The command line arguments. 244 */ 245 public static final void main( final String... args ) 246 { 247 out.printf( "Assertion is %s%n".formatted( m_AssertionOn ? "ON" : "OFF" ) ); 248 249 try 250 { 251 launch( args ); 252 } 253 catch( final Throwable t ) 254 { 255 t.printStackTrace( err ); 256 } 257 } // main() 258 259 /** 260 * {@inheritDoc} 261 */ 262 @SuppressWarnings( "ProhibitedExceptionDeclared" ) 263 @Override 264 public void start( final Stage primaryStage ) throws Exception 265 { 266 //---* Compose the scene *--------------------------------------------- 267 final var root = new VBox(); 268 269 createSimpleRangeSlider( root ); 270 createTimeRangeSlider( root ); 271 272 final var scene = new Scene( root, -1, -1 ); 273 primaryStage.setScene( scene ); 274 primaryStage.centerOnScreen(); 275 primaryStage.setOnCloseRequest( $ -> out.println( "Done!" ) ); 276 277 //---* Show the stage *-------------------------------------------- 278 primaryStage.show(); 279 } // start() 280} 281// class RangeSliderTester 282 283/* 284 * End of File 285 */