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.util; 019 020import static javafx.scene.control.ButtonType.OK; 021import static org.apiguardian.api.API.Status.STABLE; 022import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 023 024import org.apiguardian.api.API; 025import org.tquadrat.foundation.annotation.ClassVersion; 026import javafx.event.Event; 027import javafx.event.EventHandler; 028import javafx.event.EventType; 029import javafx.scene.Node; 030import javafx.scene.control.Alert; 031import javafx.scene.control.Alert.AlertType; 032import javafx.scene.control.ButtonType; 033import javafx.scene.control.DialogEvent; 034import javafx.scene.control.DialogPane; 035import javafx.stage.Modality; 036import javafx.stage.StageStyle; 037import javafx.stage.Window; 038import javafx.util.Callback; 039 040/** 041 * This class provides a fluent API to build an 042 * {@link Alert}. 043 * 044 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 045 * @version $Id: AlertBuilder.java 1110 2024-03-04 15:26:06Z tquadrat $ 046 * @since 0.4.1 047 * 048 * @UMLGraph.link 049 */ 050@SuppressWarnings( "ClassWithTooManyMethods" ) 051@ClassVersion( sourceVersion = "$Id: AlertBuilder.java 1110 2024-03-04 15:26:06Z tquadrat $" ) 052@API( status = STABLE, since = "0.4.1" ) 053public class AlertBuilder 054{ 055 /*------------*\ 056 ====** Attributes **======================================================= 057 \*------------*/ 058 /** 059 * The alert instance to build. 060 */ 061 private final Alert m_Alert; 062 063 /** 064 * Flag that prevents the re-use of the builder. 065 */ 066 private boolean m_IsBuilt; 067 068 /*--------------*\ 069 ====** Constructors **===================================================== 070 \*--------------*/ 071 /** 072 * Creates a new instance of {@code AlertBuilder}. 073 * 074 * @param type The type of the new alert. 075 */ 076 public AlertBuilder( final AlertType type ) 077 { 078 m_Alert = new Alert( requireNonNullArgument( type, "type" ) ); 079 m_IsBuilt = false; 080 } // AlertBuilder() 081 082 /** 083 * Creates a new instance of {@code AlertBuilder}. 084 * 085 * @param type The type of the new alert. 086 * @param owner The parent window for the alert. 087 */ 088 public AlertBuilder( final AlertType type, final Window owner ) 089 { 090 this( type ); 091 setOwner( owner ); 092 } // AlertBuilder() 093 094 /*---------*\ 095 ====** Methods **========================================================== 096 \*---------*/ 097 /** 098 * <p>{@summary Registers an event filter for the new alert.}</p> 099 * 100 * @param <E> The event class of the filer. 101 * @param eventType The type of the events received by the filter. 102 * @param eventFilter The event filter. 103 * @return The builder reference. 104 * @throws IllegalStateException The method 105 * {@link #build()} 106 * was already called on this builder instance. 107 * 108 * @see Alert#addEventFilter(EventType,EventHandler) 109 */ 110 public final <E extends Event> AlertBuilder addEventFilter( final EventType<E> eventType, final EventHandler<? super E> eventFilter ) throws IllegalStateException 111 { 112 checkIsBuilt(); 113 m_Alert.addEventFilter( eventType, eventFilter ); 114 115 //---* Done *---------------------------------------------------------- 116 return this; 117 } // addEventFilter() 118 119 /** 120 * <p>{@summary Registers an even filter for the new alert.}</p> 121 * 122 * @param <E> The event class of the filter. 123 * @param eventType The type of the events received by the filter. 124 * @param eventHandler The event handler. 125 * @return The builder reference. 126 * @throws IllegalStateException The method 127 * {@link #build()} 128 * was already called on this builder instance. 129 * 130 * @see Alert#addEventHandler(EventType,EventHandler) 131 */ 132 public final <E extends Event> AlertBuilder addEventHandler( final EventType<E> eventType, final EventHandler<? super E> eventHandler ) throws IllegalStateException 133 { 134 checkIsBuilt(); 135 m_Alert.addEventHandler( eventType, eventHandler ); 136 137 //---* Done *---------------------------------------------------------- 138 return this; 139 } // addEventFilter() 140 141 /** 142 * Returns the alert instance. 143 * 144 * @return The alert. 145 * @throws IllegalStateException The method {@code build()} was already 146 * called previously on this builder instance. 147 */ 148 public final Alert build() throws IllegalStateException 149 { 150 checkIsBuilt(); 151 m_IsBuilt = true; 152 153 //---* Done *---------------------------------------------------------- 154 return m_Alert; 155 } // build() 156 157 /** 158 * Throws an 159 * {@link IllegalStateException} 160 * when 161 * {@link #m_IsBuilt} 162 * is {@code true}. 163 * 164 * @throws IllegalStateException 165 * {@link #m_IsBuilt} 166 * is {@code true}. 167 */ 168 private final void checkIsBuilt() throws IllegalStateException 169 { 170 if( m_IsBuilt ) throw new IllegalStateException( "Illegal Builder state" ); 171 } // checkIsBuilt() 172 173 /** 174 * Builds the alert, calls 175 * {@link Alert#showAndWait() showAndWait()} 176 * on it and returns the result. 177 * 178 * @return {@code true} if the alert was terminated with the 179 * {@linkplain ButtonType#OK Ok button}, {@code false} in any other 180 * case. 181 * @throws IllegalStateException The method 182 * {@link #build()} 183 * was already called on this builder instance. 184 * 185 * @since 0.4.2 186 */ 187 @SuppressWarnings( "BooleanMethodNameMustStartWithQuestion" ) 188 @API( status = STABLE, since = "0.4.1" ) 189 public final boolean execute() throws IllegalStateException 190 { 191 final var retValue = build().showAndWait() 192 .filter( result -> result == OK ) 193 .isPresent(); 194 195 //---* Done *---------------------------------------------------------- 196 return retValue; 197 } // execute() 198 199 /** 200 * <p>{@summary Sets the content text for the new alert.}</p> 201 * <p>This operation can be called repeatedly; each consecutive call will 202 * overwrite the value set by the previous one.</p> 203 * 204 * @param s The content text; can be {@code null}. 205 * @return The builder reference. 206 * @throws IllegalStateException The method 207 * {@link #build()} 208 * was already called on this builder instance. 209 * 210 * @see Alert#setContentText(String) 211 */ 212 public final AlertBuilder setContentText( final String s ) throws IllegalStateException 213 { 214 checkIsBuilt(); 215 m_Alert.setContentText( s ); 216 217 //---* Done *---------------------------------------------------------- 218 return this; 219 } // setContentText() 220 221 /** 222 * <p>{@summary Sets the 223 * {@link DialogPane } 224 * for the new alert.}</p> 225 * <p>This operation can be called repeatedly; each consecutive call will 226 * overwrite the value set by the previous one.</p> 227 * 228 * @param dialogPane The dialog pane; can be {@code null}. 229 * @return The builder reference. 230 * @throws IllegalStateException The method 231 * {@link #build()} 232 * was already called on this builder instance. 233 * 234 * @see Alert#setDialogPane(DialogPane) 235 */ 236 public final AlertBuilder setDialogPane( final DialogPane dialogPane ) throws IllegalStateException 237 { 238 checkIsBuilt(); 239 m_Alert.setDialogPane( dialogPane ); 240 241 //---* Done *---------------------------------------------------------- 242 return this; 243 } // setDialogPane() 244 245 /** 246 * <p>{@summary Sets the window dimensions for the new alert.}</p> 247 * <p>This operation can be called repeatedly; each consecutive call will 248 * overwrite the values set by the previous one.</p> 249 * 250 * @param width The window width. 251 * @param height The window height. 252 * @return The builder reference. 253 * @throws IllegalStateException The method 254 * {@link #build()} 255 * was already called on this builder instance. 256 * 257 * @see Alert#setHeight(double) 258 * @see Alert#setWidth(double) 259 */ 260 public final AlertBuilder setDimensions( final double width, final double height ) throws IllegalStateException 261 { 262 setHeight( height ); 263 setWidth( width ); 264 265 //---* Done *---------------------------------------------------------- 266 return this; 267 } // setDimensions() 268 269 /** 270 * <p>{@summary Sets the graphics for the new alert.}</p> 271 * <p>This operation can be called repeatedly; each consecutive call will 272 * overwrite the value set by the previous one.</p> 273 * 274 * @param node The graphics; can be {@code null}. 275 * @return The builder reference. 276 * @throws IllegalStateException The method 277 * {@link #build()} 278 * was already called on this builder instance. 279 * 280 * @see Alert#setGraphic(Node) 281 */ 282 public final AlertBuilder setGraphic( final Node node ) throws IllegalStateException 283 { 284 checkIsBuilt(); 285 m_Alert.setGraphic( node ); 286 287 //---* Done *---------------------------------------------------------- 288 return this; 289 } // setGraphic() 290 291 /** 292 * <p>{@summary Sets the header text for the new alert.}</p> 293 * <p>This operation can be called repeatedly; each consecutive call will 294 * overwrite the value set by the previous one.</p> 295 * 296 * @param s The text for the header; can be {@code null}. 297 * @return The builder reference. 298 * @throws IllegalStateException The method 299 * {@link #build()} 300 * was already called on this builder instance. 301 * 302 * @see Alert#setHeaderText(String) 303 */ 304 public final AlertBuilder setHeaderText( final String s ) throws IllegalStateException 305 { 306 checkIsBuilt(); 307 m_Alert.setHeaderText( s ); 308 309 //---* Done *---------------------------------------------------------- 310 return this; 311 } // setHeaderText() 312 313 /** 314 * <p>{@summary Sets the window height for the new alert.}</p> 315 * <p>This operation can be called repeatedly; each consecutive call will 316 * overwrite the value set by the previous one.</p> 317 * 318 * @param height The window height. 319 * @return The builder reference. 320 * @throws IllegalStateException The method 321 * {@link #build()} 322 * was already called on this builder instance. 323 * 324 * @see Alert#setHeight(double) 325 */ 326 public final AlertBuilder setHeight( final double height ) throws IllegalStateException 327 { 328 checkIsBuilt(); 329 m_Alert.setHeight( height ); 330 331 //---* Done *---------------------------------------------------------- 332 return this; 333 } // setHeight() 334 335 /** 336 * <p>{@summary Sets the 337 * {@link Modality } 338 * for the new alert.}</p> 339 * 340 * @param modality The modality. 341 * @return The builder reference. 342 * @throws IllegalStateException The method 343 * {@link #build()} 344 * was already called on this builder instance. 345 * 346 * @see Alert#initModality(Modality) 347 */ 348 public final AlertBuilder setModality( final Modality modality ) throws IllegalStateException 349 { 350 checkIsBuilt(); 351 m_Alert.initModality( requireNonNullArgument( modality, "modality" ) ); 352 353 //---* Done *---------------------------------------------------------- 354 return this; 355 } // setModality() 356 357 /** 358 * <p>{@summary Sets the 'OnCloseRequest' event handler for the new 359 * alert.}</p> 360 * <p>This operation can be called repeatedly; each consecutive call will 361 * overwrite the value set by the previous one.</p> 362 * 363 * @param eventHandler The event handler 364 * @return The builder reference. 365 * @throws IllegalStateException The method 366 * {@link #build()} 367 * was already called on this builder instance. 368 * 369 * @see Alert#setOnCloseRequest(EventHandler) 370 */ 371 public final AlertBuilder setOnCloseRequest( final EventHandler<DialogEvent> eventHandler ) throws IllegalStateException 372 { 373 checkIsBuilt(); 374 m_Alert.setOnCloseRequest( eventHandler ); 375 376 //---* Done *---------------------------------------------------------- 377 return this; 378 } // setOnCloseRequest() 379 380 /** 381 * <p>{@summary Sets the 'OnHidden' event handler for the new alert.}</p> 382 * <p>This operation can be called repeatedly; each consecutive call will 383 * overwrite the value set by the previous one.</p> 384 * 385 * @param eventHandler The event handler 386 * @return The builder reference. 387 * @throws IllegalStateException The method 388 * {@link #build()} 389 * was already called on this builder instance. 390 * 391 * @see Alert#setOnHidden(EventHandler) 392 */ 393 public final AlertBuilder setOnHidden( final EventHandler<DialogEvent> eventHandler ) throws IllegalStateException 394 { 395 checkIsBuilt(); 396 m_Alert.setOnHidden( eventHandler ); 397 398 //---* Done *---------------------------------------------------------- 399 return this; 400 } // setHidden()} 401 402 /** 403 * <p>{@summary Sets the 'OnHiding' event handler for the new alert.}</p> 404 * <p>This operation can be called repeatedly; each consecutive call will 405 * overwrite the value set by the previous one.</p> 406 * 407 * @param eventHandler The event handler 408 * @return The builder reference. 409 * @throws IllegalStateException The method 410 * {@link #build()} 411 * was already called on this builder instance. 412 * 413 * @see Alert#setOnHiding(EventHandler) 414 */ 415 public final AlertBuilder setOnHiding( final EventHandler<DialogEvent> eventHandler ) throws IllegalStateException 416 { 417 checkIsBuilt(); 418 m_Alert.setOnHiding( eventHandler ); 419 420 //---* Done *---------------------------------------------------------- 421 return this; 422 } // setHiding()} 423 424 /** 425 * <p>{@summary Sets the 'OnShowing' event handler for the new alert.}</p> 426 * <p>This operation can be called repeatedly; each consecutive call will 427 * overwrite the value set by the previous one.</p> 428 * 429 * @param eventHandler The event handler 430 * @return The builder reference. 431 * @throws IllegalStateException The method 432 * {@link #build()} 433 * was already called on this builder instance. 434 * 435 * @see Alert#setOnShowing(EventHandler) 436 */ 437 public final AlertBuilder setOnShowing( final EventHandler<DialogEvent> eventHandler ) throws IllegalStateException 438 { 439 checkIsBuilt(); 440 m_Alert.setOnShowing( eventHandler ); 441 442 //---* Done *---------------------------------------------------------- 443 return this; 444 } // setOnShowing() 445 446 /** 447 * <p>{@summary Sets the 'OnShown' event handler for the new alert.}</p> 448 * <p>This operation can be called repeatedly; each consecutive call will 449 * overwrite the value set by the previous one.</p> 450 * 451 * @param eventHandler The event handler 452 * @return The builder reference. 453 * @throws IllegalStateException The method 454 * {@link #build()} 455 * was already called on this builder instance. 456 * 457 * @see Alert#setOnShown(EventHandler) 458 */ 459 public final AlertBuilder setOnShown( final EventHandler<DialogEvent> eventHandler ) throws IllegalStateException 460 { 461 checkIsBuilt(); 462 m_Alert.setOnShown( eventHandler ); 463 464 //---* Done *---------------------------------------------------------- 465 return this; 466 } // setOnShown() 467 468 /** 469 * Sets the parent window for the new alert. 470 * 471 * @param owner The parent window. 472 * @return The builder reference. 473 * @throws IllegalStateException The method 474 * {@link #build()} 475 * was already called on this builder instance. 476 * 477 * @see Alert#initOwner(Window) 478 * 479 * @since 0.4.2 480 */ 481 @API( status = STABLE, since = "0.4.1" ) 482 public final AlertBuilder setOwner( final Window owner ) throws IllegalStateException 483 { 484 checkIsBuilt(); 485 m_Alert.initOwner( owner ); 486 487 //---* Done *---------------------------------------------------------- 488 return this; 489 } // setOwner() 490 491 /** 492 * <p>{@summary Sets the position for the new alert.}</p> 493 * <p>This operation can be called repeatedly; each consecutive call will 494 * overwrite the values set by the previous one.</p> 495 * 496 * @param x The x position for the alert window. 497 * @param y The y position for the alert window. 498 * @return The builder reference. 499 * @throws IllegalStateException The method 500 * {@link #build()} 501 * was already called on this builder instance. 502 * 503 * @see Alert#setX(double) 504 * @see Alert#setY(double) 505 */ 506 public final AlertBuilder setPos( final double x, final double y ) throws IllegalStateException 507 { 508 setX( x ); 509 setY( y ); 510 511 //---* Done *---------------------------------------------------------- 512 return this; 513 } // setPos() 514 515 /** 516 * <p>{@summary Sets the flag that indicates whether the window for the 517 * new alert can be resized.}</p> 518 * <p>This operation can be called repeatedly; each consecutive call will 519 * overwrite the value set by the previous one.</p> 520 * 521 * @param flag {@code true} for a resizeable window, _false for a 522 * window with fixed sizes. 523 * @return The builder reference. 524 * @throws IllegalStateException The method 525 * {@link #build()} 526 * was already called on this builder instance. 527 * 528 * @see Alert#setResizable(boolean) 529 */ 530 public final AlertBuilder setResizable( final boolean flag ) throws IllegalStateException 531 { 532 checkIsBuilt(); 533 m_Alert.setResizable( flag ); 534 535 //---* Done *---------------------------------------------------------- 536 return this; 537 } // setResizable() 538 539 /** 540 * <p>{@summary Sets the result converter for the new alert.}</p> 541 * <p>This operation can be called repeatedly; each consecutive call will 542 * overwrite the value set by the previous one.</p> 543 * <p>Setting a result converter may have an impact on the behaviour of 544 * {@link #execute()}.</p> 545 * 546 * @param callback The result converter; can be {@code null}. 547 * @return The builder reference. 548 * @throws IllegalStateException The method 549 * {@link #build()} 550 * was already called on this builder instance. 551 * 552 * @see Alert#setResultConverter(Callback) 553 */ 554 public final AlertBuilder setResultConverter( final Callback<ButtonType,ButtonType> callback ) throws IllegalStateException 555 { 556 checkIsBuilt(); 557 m_Alert.setResultConverter( callback ); 558 559 //---* Done *---------------------------------------------------------- 560 return this; 561 } // setResultConverter() 562 563 /** 564 * <p>{@summary Sets the 565 * {@linkplain StageStyle style} 566 * for the new alert.}</p> 567 * 568 * @param style The style. 569 * @return The builder reference. 570 * @throws IllegalStateException The method 571 * {@link #build()} 572 * was already called on this builder instance. 573 * 574 * @see Alert#initStyle(StageStyle) 575 */ 576 public final AlertBuilder setStyle( final StageStyle style ) throws IllegalStateException 577 { 578 checkIsBuilt(); 579 m_Alert.initStyle( requireNonNullArgument( style, "style" ) ); 580 581 //---* Done *---------------------------------------------------------- 582 return this; 583 } // setStyle() 584 585 /** 586 * <p>{@summary Sets the window title for the new alert.}</p> 587 * <p>This operation can be called repeatedly; each consecutive call will 588 * overwrite the value set by the previous one.</p> 589 * 590 * @param s The window title; can be {@code null}. 591 * @return The builder reference. 592 * @throws IllegalStateException The method 593 * {@link #build()} 594 * was already called on this builder instance. 595 * 596 * @see Alert#setTitle(String) 597 */ 598 public final AlertBuilder setTitle( final String s ) throws IllegalStateException 599 { 600 checkIsBuilt(); 601 m_Alert.setTitle( s ); 602 603 //---* Done *---------------------------------------------------------- 604 return this; 605 } // setTitle() 606 607 /** 608 * <p>{@summary Sets the window width for the new alert.}</p> 609 * <p>This operation can be called repeatedly; each consecutive call will 610 * overwrite the value set by the previous one.</p> 611 * 612 * @param width The window width. 613 * @return The builder reference. 614 * @throws IllegalStateException The method 615 * {@link #build()} 616 * was already called on this builder instance. 617 * 618 * @see Alert#setWidth(double) 619 */ 620 public final AlertBuilder setWidth( final double width ) throws IllegalStateException 621 { 622 checkIsBuilt(); 623 m_Alert.setWidth( width ); 624 625 //---* Done *---------------------------------------------------------- 626 return this; 627 } // setWidth() 628 629 /** 630 * <p>{@summary Sets the x position for the new alert.}</p> 631 * <p>This operation can be called repeatedly; each consecutive call will 632 * overwrite the value set by the previous one.</p> 633 * 634 * @param x The x position. 635 * @return The builder reference. 636 * @throws IllegalStateException The method 637 * {@link #build()} 638 * was already called on this builder instance. 639 * 640 * @see Alert#setX(double) 641 */ 642 public final AlertBuilder setX( final double x ) throws IllegalStateException 643 { 644 checkIsBuilt(); 645 m_Alert.setX( x ); 646 647 //---* Done *---------------------------------------------------------- 648 return this; 649 } // setX() 650 651 /** 652 * <p>{@summary Sets the y position for the new alert.}</p> 653 * <p>This operation can be called repeatedly; each consecutive call will 654 * overwrite the value set by the previous one.</p> 655 * 656 * @param y The y position. 657 * @return The builder reference. 658 * @throws IllegalStateException The method 659 * {@link #build()} 660 * was already called on this builder instance. 661 * 662 * @see Alert#setX(double) 663 */ 664 public final AlertBuilder setY( final double y ) throws IllegalStateException 665 { 666 checkIsBuilt(); 667 m_Alert.setY( y ); 668 669 //---* Done *---------------------------------------------------------- 670 return this; 671 } // setY() 672} 673// class AlertBuilder 674 675/* 676 * End of File 677 */