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 */