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; 019 020import static java.lang.Boolean.FALSE; 021import static java.util.Locale.ROOT; 022import static javafx.beans.binding.Bindings.createObjectBinding; 023import static org.apiguardian.api.API.Status.INTERNAL; 024import static org.apiguardian.api.API.Status.STABLE; 025import static org.tquadrat.foundation.fx.control.RangeSlider.StyleableProperties.SNAP_TO_TICKS; 026import static org.tquadrat.foundation.fx.control.TimeSlider.StyleableProperties.GRANULARITY; 027import static org.tquadrat.foundation.fx.control.TimeSlider.StyleableProperties.TIME_ZONE; 028import static org.tquadrat.foundation.lang.Objects.mapFromNull; 029import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 030 031import java.time.Duration; 032import java.time.LocalDate; 033import java.time.LocalTime; 034import java.time.OffsetTime; 035import java.time.ZoneId; 036import java.time.ZonedDateTime; 037import java.util.List; 038 039import org.apiguardian.api.API; 040import org.tquadrat.foundation.annotation.ClassVersion; 041import org.tquadrat.foundation.annotation.UtilityClass; 042import org.tquadrat.foundation.exception.PrivateConstructorForStaticClassCalledError; 043import org.tquadrat.foundation.fx.control.skin.TimeSliderSkin; 044import org.tquadrat.foundation.fx.css.TimeZoneConverter; 045import org.tquadrat.foundation.fx.internal.FoundationFXControl; 046import org.tquadrat.foundation.lang.Objects; 047import javafx.beans.binding.ObjectBinding; 048import javafx.beans.property.BooleanProperty; 049import javafx.beans.property.ObjectProperty; 050import javafx.beans.property.Property; 051import javafx.beans.property.ReadOnlyObjectProperty; 052import javafx.beans.property.SimpleObjectProperty; 053import javafx.beans.value.ChangeListener; 054import javafx.beans.value.ObservableValue; 055import javafx.css.CssMetaData; 056import javafx.css.ParsedValue; 057import javafx.css.SimpleStyleableBooleanProperty; 058import javafx.css.SimpleStyleableObjectProperty; 059import javafx.css.StyleConverter; 060import javafx.css.Styleable; 061import javafx.css.StyleableBooleanProperty; 062import javafx.css.StyleableObjectProperty; 063import javafx.css.StyleableProperty; 064import javafx.css.converter.BooleanConverter; 065import javafx.scene.control.Skin; 066import javafx.scene.text.Font; 067 068/** 069 * <p>{@summary A {@code TimeSlider} is basically a 070 * {@link RangeSlider} 071 * for the input of times and durations.}</p> 072 * <p>In fact, this implementation wraps a {@code RangeSlider} instance that 073 * is configured in a special way.</p> 074 * 075 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 076 * @version $Id: TimeSlider.java 1121 2024-03-16 16:51:23Z tquadrat $ 077 * @since 0.4.6 078 * 079 * @UMLGraph.link 080 */ 081@SuppressWarnings( {"ClassWithTooManyMethods", "ClassWithTooManyConstructors", "ClassWithTooManyFields"} ) 082@ClassVersion( sourceVersion = "$Id: TimeSlider.java 1121 2024-03-16 16:51:23Z tquadrat $" ) 083@API( status = STABLE, since = "0.4.6" ) 084public final class TimeSlider extends FoundationFXControl 085{ 086 /*---------------*\ 087 ====** Inner Classes **==================================================== 088 \*---------------*/ 089 /** 090 * The granularity for the 091 * {@link TimeSlider}. 092 * 093 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 094 * @version $Id: TimeSlider.java 1121 2024-03-16 16:51:23Z tquadrat $ 095 * @since 0.4.6 096 */ 097 @ClassVersion( sourceVersion = "$Id: TimeSlider.java 1121 2024-03-16 16:51:23Z tquadrat $" ) 098 @API( status = STABLE, since = "0.4.6" ) 099 public static enum Granularity 100 { 101 /*------------------*\ 102 ====** Enum Declaration **============================================= 103 \*------------------*/ 104 /** 105 * Only full hours can be selected. 106 */ 107 ONE_HOUR( -1 ), 108 109 /** 110 * Half hour steps are possible. 111 */ 112 HALF_HOUR( 1 ), 113 114 /** 115 * 20 minutes steps can be selected. 116 */ 117 TWENTY_MINUTES( 2 ), 118 119 /** 120 * Quarter hour steps are possible. 121 */ 122 QUARTER_HOUR( 3 ), 123 124 /** 125 * 10 minutes steps can be selected. 126 */ 127 TEN_MINUTES( 5 ), 128 129 /** 130 * 5 minutes steps can be selected. 131 */ 132 FIVE_MINUTES( 11 ), 133 134 /** 135 * One minute steps can be selected. 136 */ 137 ONE_MINUTE( 59 ); 138 139 140 /*------------*\ 141 ====** Attributes **=================================================== 142 \*------------*/ 143 /** 144 * The minor tick count for this granularity. 145 */ 146 private final int m_MinorTickCount; 147 148 /*--------------*\ 149 ====** Constructors **================================================= 150 \*--------------*/ 151 /** 152 * Creates a new instance of {@code Granularity}. 153 * 154 * @param tickCount The minor tick count for this granularity. 155 */ 156 private Granularity( final int tickCount ) 157 { 158 m_MinorTickCount = tickCount; 159 } // Granularity() 160 161 /*---------*\ 162 ====** Methods **====================================================== 163 \*---------*/ 164 /** 165 * Returns the minor tick count for this granularity. 166 * 167 * @return The tick count. 168 */ 169 public final int getMinorTickCount() { return m_MinorTickCount; } 170 } 171 // enum Granularity 172 173 /** 174 * An implementation of 175 * {@link javafx.css.StyleConverter} 176 * for the 177 * {@link Granularity}. 178 * 179 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 180 * @version $Id: TimeSlider.java 1121 2024-03-16 16:51:23Z tquadrat $ 181 * @since 0.4.6 182 * 183 * @UMLGraph.link 184 */ 185 @ClassVersion( sourceVersion = "$Id: TimeSlider.java 1121 2024-03-16 16:51:23Z tquadrat $" ) 186 @API( status = INTERNAL, since = "0.4.6" ) 187 private static final class GranularityConverter extends StyleConverter<String,Granularity> 188 { 189 /*--------------*\ 190 ====** Constructors **================================================= 191 \*--------------*/ 192 /** 193 * Creates a new instance of {@code GranularityConverter}. 194 */ 195 public GranularityConverter() { /* Just exists */ } 196 197 /*---------*\ 198 ====** Methods **====================================================== 199 \*---------*/ 200 /** 201 * {@inheritDoc} 202 */ 203 @Override 204 public final Granularity convert( final ParsedValue<String,Granularity> value, final Font font ) 205 { 206 Granularity retValue; 207 try 208 { 209 retValue = Granularity.valueOf( ((String) value.getValue()).toUpperCase( ROOT ) ); 210 } 211 catch( final IllegalArgumentException ignored ) 212 { 213 retValue = Granularity.QUARTER_HOUR; 214 } 215 216 //---* Done *------------------------------------------------------ 217 return retValue; 218 } // convert() 219 220 /** 221 * {@inheritDoc} 222 */ 223 @Override 224 public final String toString() { return "GranularityConverter"; } 225 } 226 // class TimeZoneConverter 227 228 /** 229 * The styleable properties for 230 * {@link TimeSlider}. 231 * 232 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 233 * @version $Id: TimeSlider.java 1121 2024-03-16 16:51:23Z tquadrat $ 234 * @since 0.4.6 235 */ 236 @SuppressWarnings( {"ProtectedInnerClass", "InnerClassTooDeeplyNested", "AnonymousInnerClass"} ) 237 @UtilityClass 238 @ClassVersion( sourceVersion = "$Id: TimeSlider.java 1121 2024-03-16 16:51:23Z tquadrat $" ) 239 @API( status = STABLE, since = "0.4.6" ) 240 protected static final class StyleableProperties 241 { 242 /*------------------------*\ 243 ====** Static Initialisations **======================================= 244 \*------------------------*/ 245 /** 246 * The CSS attribute for {@code GRANULARITY}. 247 * 248 * @see #granularityProperty() 249 */ 250 public static final CssMetaData<TimeSlider,Granularity> GRANULARITY = new CssMetaData<>( "-fx-granularity", new GranularityConverter(), Granularity.QUARTER_HOUR ) 251 { 252 /** 253 * {@inheritDoc} 254 */ 255 @Override 256 public final StyleableProperty<Granularity> getStyleableProperty( final TimeSlider styleable ) { return styleable.m_GranularityProperty; } 257 258 /** 259 * {@inheritDoc} 260 */ 261 @Override 262 public final boolean isSettable( final TimeSlider styleable ) { return !styleable.m_GranularityProperty.isBound(); } 263 }; 264 265 /** 266 * The CSS attribute for {@code SNAP_TO_TICKS}. 267 * 268 * @see #snapToTicksProperty() 269 */ 270 public static final CssMetaData<TimeSlider,Boolean> SNAP_TO_TICKS = new CssMetaData<>( "-fx-snap-to-ticks", BooleanConverter.getInstance(), FALSE ) 271 { 272 /** 273 * {@inheritDoc} 274 */ 275 @Override 276 public final StyleableProperty<Boolean> getStyleableProperty( final TimeSlider styleable ) { return styleable.m_SnapToTicksProperty; } 277 278 /** 279 * {@inheritDoc} 280 */ 281 @Override 282 public final boolean isSettable( final TimeSlider styleable ) { return !styleable.m_SnapToTicksProperty.isBound(); } 283 }; 284 285 /** 286 * The CSS attribute for the {@code TIME_ZONE}. 287 * 288 * @see #timeZoneProperty() 289 */ 290 public static final CssMetaData<TimeSlider,ZoneId> TIME_ZONE = new CssMetaData<>( "-fx-timezone", TimeZoneConverter.getInstance(), ZoneId.systemDefault() ) 291 { 292 /** 293 * {@inheritDoc} 294 */ 295 @Override 296 public final StyleableProperty<ZoneId> getStyleableProperty( final TimeSlider styleable ) { return styleable.m_TimeZoneProperty; } 297 298 /** 299 * {@inheritDoc} 300 */ 301 @Override 302 public final boolean isSettable( final TimeSlider styleable ) { return !styleable.m_TimeZoneProperty.isBound(); } 303 }; 304 305 /** 306 * The CSS attributes for 307 * {@link TimeSlider}. 308 */ 309 @SuppressWarnings( "StaticCollection" ) 310 public static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES = List.of( GRANULARITY, SNAP_TO_TICKS, TIME_ZONE ); 311 312 /*--------------*\ 313 ====** Constructors **================================================= 314 \*--------------*/ 315 /** 316 * No instance allowed for this class! 317 */ 318 private StyleableProperties() { throw new PrivateConstructorForStaticClassCalledError( RangeSlider.StyleableProperties.class ); } 319 } 320 // class StyleableProperties 321 322 /*-----------*\ 323 ====** Constants **======================================================== 324 \*-----------*/ 325 326 /*------------*\ 327 ====** Attributes **======================================================= 328 \*------------*/ 329 /** 330 * <p>{@summary The property for the day for that the times should be set.} 331 * The value for this property may not be {@code null}.</p> 332 */ 333 @SuppressWarnings( "AnonymousInnerClass" ) 334 private final ObjectProperty<LocalDate> m_DayProperty = new SimpleObjectProperty<>( this, "day", LocalDate.now() ) 335 { 336 /** 337 * {@inheritDoc} 338 */ 339 @Override 340 public final void set( final LocalDate newValue ) 341 { 342 super.set( requireNonNullArgument( newValue, "newValue" ) ); 343 } // set() 344 }; 345 346 /** 347 * <p>{@summary The property that holds the duration of the time period 348 * between 349 * {@linkplain #getLowValue() low} 350 * and 351 * {@linkplain #getHighValue() high}.}</p> 352 */ 353 @SuppressWarnings( "ThisEscapedInObjectConstruction" ) 354 private final ObjectProperty<Duration> m_DurationProperty = new SimpleObjectProperty<>( this, "duration" ); 355 356 /** 357 * <p>{@summary The property that holds the granularity for the 358 * {@code TimeSlider}.} The granularity determines the steps size for the 359 * time selection. 360 */ 361 @SuppressWarnings( "ThisEscapedInObjectConstruction" ) 362 private final StyleableObjectProperty<Granularity> m_GranularityProperty = new SimpleStyleableObjectProperty<>( GRANULARITY, this, "granularity", Granularity.QUARTER_HOUR ); 363 364 /** 365 * <p>{@summary The high value property.} It represents the current 366 * position of the high value thumb, and is within the allowable range as 367 * specified by the 368 * {@link #minDisplayProperty() min} 369 * and 370 * {@link #maxDisplayProperty() max} 371 * properties.</p> 372 */ 373 @SuppressWarnings( "ThisEscapedInObjectConstruction" ) 374 private final ObjectProperty<OffsetTime> m_HighValueProperty = new SimpleObjectProperty<>( this, "highValue" ); 375 376 /** 377 * <p>{@summary The low value property.} It represents the current 378 * position of the low value thumb, and is within the allowable range as 379 * specified by the 380 * {@link #minDisplayProperty() min} 381 * and 382 * {@link #maxDisplayProperty() max} 383 * properties.</p> 384 */ 385 @SuppressWarnings( "ThisEscapedInObjectConstruction" ) 386 private final ObjectProperty<OffsetTime> m_LowValueProperty = new SimpleObjectProperty<>(this, "lowValue" ); 387 388 /** 389 * The property for the maximum displayed value of this 390 * {@code TimeSlider}. 391 */ 392 @SuppressWarnings( "AnonymousInnerClass" ) 393 private final ObjectProperty<LocalTime> m_MaxDisplayProperty = new SimpleObjectProperty<>(this, "maxDisplay" ) 394 { 395 /** 396 * {@inheritDoc} 397 */ 398 @Override 399 protected final void invalidated() 400 { 401 final var min = getMin(); 402 if( Objects.isNull( min ) || get().isBefore( min ) ) setMin( get() ); 403 } // invalidated() 404 }; 405 406 /** 407 * <p>{@summary The property for the maximum value of this 408 * {@code TimeSlider}.}</p> 409 * <p>This property is fixed bound to the properties 410 * {@link #maxDisplayProperty()}, 411 * {@link #dayProperty()} 412 * {@link #timeZoneProperty()}.</p> 413 */ 414 @SuppressWarnings( "ThisEscapedInObjectConstruction" ) 415 private final ObjectProperty<ZonedDateTime> m_MaxValueProperty = new SimpleObjectProperty<>(this, "maxValue" ); 416 417 /** 418 * The binding that updates the internal 419 * {@link #m_MaxValueProperty}. 420 */ 421 private final ObjectBinding<ZonedDateTime> m_MaxValueBinding; 422 423 /** 424 * The property for the minimum displayed value of this 425 * {@code TimeSlider}. 426 */ 427 @SuppressWarnings( "AnonymousInnerClass" ) 428 private final ObjectProperty<LocalTime> m_MinDisplayProperty = new SimpleObjectProperty<>( this, "minDisplay" ) 429 { 430 /** 431 * {@inheritDoc} 432 */ 433 @Override 434 protected final void invalidated() 435 { 436 final var max = getMax(); 437 if( Objects.isNull( max ) || get().isAfter( max ) ) setMax( get() ); 438 } // invalidated() 439 }; 440 441 /** 442 * <p>{@summary The property for the minimum value of this 443 * {@code TimeSlider}.}</p> 444 * <p>This property is fixed bound to the properties 445 * {@link #minDisplayProperty()}, 446 * {@link #dayProperty()} 447 * {@link #timeZoneProperty()}.</p> 448 */ 449 @SuppressWarnings( "ThisEscapedInObjectConstruction" ) 450 private final ObjectProperty<ZonedDateTime> m_MinValueProperty = new SimpleObjectProperty<>( this, "minValue" ); 451 452 /** 453 * The binding that updates the internal 454 * {@link #m_MinValueProperty}. 455 */ 456 private final ObjectBinding<ZonedDateTime> m_MinValueBinding; 457 458 /** 459 * The property for the flag that controls whether the thumbs will snap to 460 * the tick marks. 461 */ 462 @SuppressWarnings( "ThisEscapedInObjectConstruction" ) 463 private final StyleableBooleanProperty m_SnapToTicksProperty = new SimpleStyleableBooleanProperty( SNAP_TO_TICKS, this, "snapToTicks", true ); 464 465 /** 466 * The property for the time zone that is used to calculate the offset for 467 * the times. 468 */ 469 @SuppressWarnings( "ThisEscapedInObjectConstruction" ) 470 private final StyleableObjectProperty<ZoneId> m_TimeZoneProperty = new SimpleStyleableObjectProperty<>( TIME_ZONE,this, "timeZone", ZoneId.systemDefault() ); 471 472 /*------------------------*\ 473 ====** Static Initialisations **=========================================== 474 \*------------------------*/ 475 476 /*--------------*\ 477 ====** Constructors **===================================================== 478 \*--------------*/ 479 /** 480 * <p>{@summary Creates a new instance of {@code TimeSlider} for the 481 * current day and the default time zone.} The time range will be set from 482 * 00:00 to 23:59.</p> 483 * <p>Once created, the time zone for the {@code TimeSlider} cannot be 484 * modified.</p> 485 */ 486 public TimeSlider() 487 { 488 this( LocalDate.now() ); 489 } // TimeSlider() 490 491 /** 492 * <p>{@summary Creates a new instance of {@code TimeSlider} for the given 493 * day and the default time zone.} The time range will be set from 00:00 494 * to 23:59.</p> 495 * <p>Once created, the time zone for the {@code TimeSlider} cannot be 496 * modified.</p> 497 * 498 * @param day The day for the time slider. 499 */ 500 public TimeSlider( final LocalDate day ) 501 { 502 this( requireNonNullArgument( day, "day" ), ZoneId.systemDefault() ); 503 } // TimeSlider() 504 505 /** 506 * <p>{@summary Creates a new instance of {@code TimeSlider} for the given 507 * day and the given time zone.} The time range will be set from 00:00 508 * to 23:59.</p> 509 * <p>Once created, the time zone for the {@code TimeSlider} cannot be 510 * modified.</p> 511 * 512 * @param day The day for the time slider. 513 * @param timeZone The time zone. 514 */ 515 public TimeSlider( final LocalDate day, final ZoneId timeZone ) 516 { 517 this( requireNonNullArgument( day, "day" ), requireNonNullArgument( timeZone, "timeZone" ), LocalTime.MIN, LocalTime.MAX.withSecond( 0 ).withNano( 0 ) ); 518 } // TimeSlider() 519 520 /** 521 * <p>{@summary Creates a new instance of {@code TimeSlider} for the 522 * current day and the default time zone.}</p> 523 * <p>Once created, the time zone for the {@code TimeSlider} cannot be 524 * modified.</p> 525 * 526 * @param min The minimum displayed time for the slider. 527 * @param max The maximum displayed time for the slider. 528 * 529 */ 530 public TimeSlider( final LocalTime min, final LocalTime max ) 531 { 532 this( LocalDate.now(), ZoneId.systemDefault(), requireNonNullArgument( min, "min" ), requireNonNullArgument( max, "max" ) ); 533 } // TimeSlider() 534 535 /** 536 * <p>{@summary Creates a new instance of {@code TimeSlider} for the 537 * given day and the default time zone.}</p> 538 * <p>Once created, the time zone for the {@code TimeSlider} cannot be 539 * modified.</p> 540 * 541 * @param day The day for the time slider. 542 * @param min The minimum displayed time for the slider. 543 * @param max The maximum displayed time for the slider. 544 * 545 */ 546 public TimeSlider( final LocalDate day, final LocalTime min, final LocalTime max ) 547 { 548 this( requireNonNullArgument( day, "day" ), ZoneId.systemDefault(), requireNonNullArgument( min, "min" ), requireNonNullArgument( max, "max" ) ); 549 } // TimeSlider() 550 551 /** 552 * <p>{@summary Creates a new instance of {@code TimeSlider} for the 553 * given day and the given time zone.}</p> 554 * <p>Once created, the time zone for the {@code TimeSlider} cannot be 555 * modified.</p> 556 * 557 * @param day The day for the time slider. 558 * @param timeZone The time zone. 559 * @param min The minimum displayed time for the slider. 560 * @param max The maximum displayed time for the slider. 561 * 562 */ 563 public TimeSlider( final LocalDate day, final ZoneId timeZone, final LocalTime min, final LocalTime max ) 564 { 565 super(); 566 567 //---* Apply the arguments *------------------------------------------- 568 setDay( day ); 569 m_TimeZoneProperty.set( requireNonNullArgument( timeZone, "timeZone" ) ); 570 setMax( requireNonNullArgument( max, "max" ) ); 571 setMin( requireNonNullArgument( min, "min" ) ); 572 573 m_MaxValueProperty.set( day.atTime( max ).atZone( timeZone ) ); 574 m_MinValueProperty.set( day.atTime( min ).atZone( timeZone ) ); 575 576 //---* Set the defaults *---------------------------------------------- 577 setHighValue( m_MaxValueProperty.get().toOffsetDateTime().toOffsetTime() ); 578 setLowValue( m_MinValueProperty.get().toOffsetDateTime().toOffsetTime() ); 579 580 //---* Create and apply the bindings *--------------------------------- 581 m_MinValueBinding = createObjectBinding( () -> getDay().atTime( getMin() ).atZone( getTimeZone() ), m_MinDisplayProperty ); 582 m_MinValueProperty.bind( m_MinValueBinding ); 583 584 m_MaxValueBinding = createObjectBinding( () -> getDay().atTime( getMax() ).atZone( getTimeZone() ), m_MaxDisplayProperty ); 585 m_MaxValueProperty.bind( m_MaxValueBinding ); 586 587 m_DayProperty.addListener( this::dayChangeListener ); 588 589 final var durationBinding = createObjectBinding( () -> Duration.between( getLowValue().atDate( getDay() ), getHighValue().atDate( getDay() ) ), m_LowValueProperty, m_HighValueProperty ); 590 m_DurationProperty.bind( durationBinding ); 591 592 //---* Apply the skin *------------------------------------------------ 593 setSkin( createDefaultSkin() ); 594 } // TimeSlider() 595 596 /*---------*\ 597 ====** Methods **========================================================== 598 \*---------*/ 599 /** 600 * {@inheritDoc} 601 */ 602 @Override 603 protected final Skin<?> createDefaultSkin() 604 { 605 final var retValue = new TimeSliderSkin( this ); 606 607 //---* Done *---------------------------------------------------------- 608 return retValue; 609 } // createDefaultSkin() 610 611 /** 612 * The implementation of 613 * {@link ChangeListener} 614 * that responds on changes to the 615 * {@linkplain #dayProperty() day property}. 616 * 617 * @param observable The observable. 618 * @param oldValue The old value. 619 * @param newValue The new value. 620 */ 621 @SuppressWarnings( "TypeParameterExtendsFinalClass" ) 622 private final void dayChangeListener( final ObservableValue<? extends LocalDate> observable, final LocalDate oldValue, final LocalDate newValue ) 623 { 624 if( !Objects.equals( oldValue, newValue ) ) 625 { 626 final var lowValue = m_LowValueProperty.get(); 627 final var highValue = m_HighValueProperty.get(); 628 629 if( oldValue.isBefore( newValue ) ) 630 { 631 m_MaxValueBinding.invalidate(); 632 m_MaxValueProperty.get(); 633 m_MinValueBinding.invalidate(); 634 m_MinValueProperty.get(); 635 } 636 else 637 { 638 m_MinValueBinding.invalidate(); 639 m_MinValueProperty.get(); 640 m_MaxValueBinding.invalidate(); 641 m_MaxValueProperty.get(); 642 } 643 644 setHighValue( highValue ); 645 setLowValue( lowValue ); 646 } 647 } // dayChangeListener() 648 649 /** 650 * <p>{@summary Returns a reference to the property that holds the day for 651 * the times.}</p> 652 * 653 * @return The property reference. 654 */ 655 public final ObjectProperty<LocalDate> dayProperty() { return m_DayProperty; } 656 657 /** 658 * <p>{@summary Returns a reference to the property that holds the 659 * duration of the time period between 660 * {@linkplain #getLowValue() low} 661 * and 662 * {@linkplain #getHighValue() high}.}</p> 663 * 664 * @return The property reference. 665 */ 666 public final ReadOnlyObjectProperty<Duration> durationProperty(){ return m_DurationProperty; } 667 668 /** 669 * Returns the day for the times. 670 * 671 * @return The day. 672 */ 673 public final LocalDate getDay() { return m_DayProperty.get(); } 674 675 /** 676 * Returns the duration for the time slider. 677 * 678 * @return The duration. 679 */ 680 public final Duration getDuration() { return m_DurationProperty.get(); } 681 682 /** 683 * Returns the granularity for the time slider. 684 * 685 * @return The granularity. 686 */ 687 public final Granularity getGranularity() { return m_GranularityProperty.get(); } 688 689 /** 690 * Returns the current high value for the time slider. 691 * 692 * @return The high value. 693 */ 694 public final OffsetTime getHighValue() { return m_HighValueProperty.get(); } 695 696 /** 697 * Returns the current low value for the range slider. 698 * 699 * @return The low value. 700 */ 701 public final OffsetTime getLowValue() { return m_LowValueProperty.get(); } 702 703 /** 704 * <p>{@summary Returns the maximum displayed value for this 705 * {@code TimeSlider}.}</p> 706 * <p>23:59:59 is returned if the maximum value has never been set 707 * explicitly.</p> 708 * 709 * @return The maximum value for this time slider. 710 */ 711 public final LocalTime getMax() { return m_MaxDisplayProperty.get(); } 712 713 /** 714 * <p>{@summary Returns the minimum displayed value for this 715 * {@code TimeSlider}.}</p> 716 * <p>00:00:00 is returned if the minimum has never been set 717 * explicitly.</p> 718 * 719 * @return The minimum value for this time slider. 720 */ 721 public final LocalTime getMin() { return m_MinDisplayProperty.get(); } 722 723 /** 724 * Returns the time zone that is used to calculate the offset for the 725 * times. 726 * 727 * @return The time zone. 728 */ 729 public final ZoneId getTimeZone() { return m_TimeZoneProperty.get(); } 730 731 /** 732 * <p>{@summary Returns a reference to the property that holds the 733 * granularity for the {@code TimeSlider}.} The granularity determines the 734 * steps size for the time selection. 735 * 736 * @return The property reference. 737 */ 738 public final StyleableObjectProperty<Granularity> granularityProperty() { return m_GranularityProperty; } 739 740 /** 741 * <p>{@summary Returns a reference to the property that holds the high 742 * value.}</p> 743 * <p>The high value property represents the current position of the high 744 * value thumb, and is within the allowable range as specified by the 745 * {@link #minDisplayProperty() min} 746 * and 747 * {@link #maxDisplayProperty() max} 748 * properties.</p> 749 * 750 * @return The property reference. 751 */ 752 public final ObjectProperty<OffsetTime> highValueProperty() { return m_HighValueProperty; } 753 754 /** 755 * Returns the flag that controls whether the thumbs will snap to the tick 756 * marks. 757 * 758 * @return {@code true} if the thumbs will snap to the tick marks, 759 * otherwise {@code false}. 760 * 761 * @see #snapToTicksProperty() 762 */ 763 public final boolean isSnapToTicks() { return m_SnapToTicksProperty.get(); } 764 765 /** 766 * <p>{@summary Returns a reference to the property that holds the low 767 * value.}</p> 768 * <p>The low value property represents the current position of the low 769 * value thumb, and is within the allowable range as specified by the 770 * {@link #minDisplayProperty() min} 771 * and 772 * {@link #maxDisplayProperty() max} 773 * properties.</p> 774 * 775 * @return The property reference. 776 */ 777 public final ObjectProperty<OffsetTime> lowValueProperty() { return m_LowValueProperty; } 778 779 /** 780 * <p>{@summary Returns a reference to the property that holds the maximum 781 * displayed value for this {@code TimeSlider}.}</p> 782 * 783 * @return The property reference. 784 */ 785 public final ObjectProperty<LocalTime> maxDisplayProperty() { return m_MaxDisplayProperty; } 786 787 /** 788 * <p>{@summary Returns a reference to the property that holds the maximum 789 * value for this {@code TimeSlider}.}</p> 790 * 791 * @return The property reference. 792 */ 793 public final ReadOnlyObjectProperty<ZonedDateTime> maxValueProperty() { return m_MaxValueProperty; } 794 795 /** 796 * <p>{@summary Returns a reference to the property that holds the minimum 797 * value for this {@code TimeSlider}.}</p> 798 * 799 * @return The property reference. 800 */ 801 public final Property<LocalTime> minDisplayProperty() { return m_MinDisplayProperty; } 802 803 /** 804 * <p>{@summary Returns a reference to the property that holds the minimum 805 * for this {@code TimeSlider}.}</p> 806 * 807 * @return The property reference. 808 */ 809 public final ReadOnlyObjectProperty<ZonedDateTime> minValueProperty() { return m_MinValueProperty; } 810 811 /** 812 * Sets the day for this {@code TimeSlider}. 813 * 814 * @param day The day. 815 */ 816 public final void setDay( final LocalDate day ) { m_DayProperty.set( requireNonNullArgument( day, "day" ) ); } 817 818 /** 819 * Sets the granularity for this {@code TimeSlider}. 820 * 821 * @param granularity The granularity. 822 */ 823 public final void setGranularity( final Granularity granularity ) 824 { 825 m_GranularityProperty.set( mapFromNull( granularity, Granularity.QUARTER_HOUR ) ); 826 } // setGranularity() 827 828 /** 829 * Sets the high value for this {@code TimeSlider}, which may or may not 830 * be clamped to be within the allowable range as specified by the 831 * {@link #minDisplayProperty() min} 832 * and 833 * {@link #maxDisplayProperty() max} 834 * properties. 835 * 836 * @param highValue The value. 837 */ 838 public final void setHighValue( final OffsetTime highValue ) { m_HighValueProperty.set( highValue ); } 839 840 /** 841 * Sets the high value for this {@code TimeSlider}, which may or may not 842 * be clamped to be within the allowable range as specified by the 843 * {@link #minDisplayProperty() min} 844 * and 845 * {@link #maxDisplayProperty() max} 846 * properties. 847 * 848 * @param highValue The value. 849 */ 850 public final void setHighValue( final LocalTime highValue ) 851 { 852 final var offsetTime = getDay() 853 .atTime( requireNonNullArgument( highValue, "highValue" ) ) 854 .atZone( getTimeZone() ) 855 .toOffsetDateTime() 856 .toOffsetTime(); 857 setHighValue( offsetTime ); 858 } // setHighValue() 859 860 /** 861 * Sets the low value for this {@code TimeSlider}, which may or may not be 862 * clamped to be within the allowable range as specified by the 863 * {@link #minDisplayProperty() min} 864 * and 865 * {@link #maxDisplayProperty() max} 866 * properties. 867 * 868 * @param lowValue The value. 869 */ 870 public final void setLowValue( final OffsetTime lowValue ) { m_LowValueProperty.set( lowValue ); } 871 872 /** 873 * Sets the low value for this {@code TimeSlider}, which may or may not be 874 * clamped to be within the allowable range as specified by the 875 * {@link #minDisplayProperty() min} 876 * and 877 * {@link #maxDisplayProperty() max} 878 * properties. 879 * 880 * @param lowValue The value. 881 */ 882 public final void setLowValue( final LocalTime lowValue ) 883 { 884 final var offsetTime = getDay() 885 .atTime( requireNonNullArgument( lowValue, "lowValue" ) ) 886 .atZone( getTimeZone() ) 887 .toOffsetDateTime() 888 .toOffsetTime(); 889 setLowValue( offsetTime ); 890 } // setLowValue() 891 892 /** 893 * Sets the maximum displayed value for this {@code TimeSlider}. 894 * 895 * @param max The new value. 896 */ 897 public final void setMax( final LocalTime max ) { m_MaxDisplayProperty.set( max ); } 898 899 /** 900 * Sets the minimum displayed value for this {@code TimeSlider}. 901 * 902 * @param min The new value 903 */ 904 public final void setMin( final LocalTime min ) { m_MinDisplayProperty.set( min ); } 905 906 /** 907 * Sets the flag that controls whether the thumbs will snap to the tick 908 * marks. 909 * 910 * @param flag {@code true} if the thumbs snaps to the tick marks, 911 * {@code false} if not. 912 * 913 * @see #snapToTicksProperty() 914 */ 915 public final void setSnapToTicks( final boolean flag ) { m_SnapToTicksProperty.set( flag ); } 916 917 /** 918 * <p>{@summary Returns a reference to the property that holds the flag 919 * that indicates whether the 920 * {@linkplain #lowValueProperty() low value}/{@linkplain #highValueProperty() high value} 921 * thumbs of the {@code TimeSlider} should always be aligned with the 922 * tick marks.} This is honored even if the tick marks are not shown.</p> 923 * 924 * @return The property reference. 925 */ 926 public final BooleanProperty snapToTicksProperty() { return m_SnapToTicksProperty; } 927 928 /** 929 * <p>{@summary Returns a reference to the property that holds the time 930 * zone that is used to determine the offset for the times.}</p> 931 * 932 * @return The property reference. 933 */ 934 public final ReadOnlyObjectProperty<ZoneId> timeZoneProperty() { return m_TimeZoneProperty; } 935} 936// class TimeSlider 937 938/* 939 * End of File 940 */