001/* 002 * ============================================================================ 003 * Copyright © 2002-2025 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 1151 2025-10-01 21:32:15Z tquadrat $ 046 * @since 0.4.1 047 * 048 * @UMLGraph.link 049 */ 050@SuppressWarnings( "ClassWithTooManyMethods" ) 051@ClassVersion( sourceVersion = "$Id: AlertBuilder.java 1151 2025-10-01 21:32:15Z 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 @SuppressWarnings( "UnusedReturnValue" ) 327 public final AlertBuilder setHeight( final double height ) throws IllegalStateException 328 { 329 checkIsBuilt(); 330 m_Alert.setHeight( height ); 331 332 //---* Done *---------------------------------------------------------- 333 return this; 334 } // setHeight() 335 336 /** 337 * <p>{@summary Sets the 338 * {@link Modality } 339 * for the new alert.}</p> 340 * 341 * @param modality The modality. 342 * @return The builder reference. 343 * @throws IllegalStateException The method 344 * {@link #build()} 345 * was already called on this builder instance. 346 * 347 * @see Alert#initModality(Modality) 348 */ 349 public final AlertBuilder setModality( final Modality modality ) throws IllegalStateException 350 { 351 checkIsBuilt(); 352 m_Alert.initModality( requireNonNullArgument( modality, "modality" ) ); 353 354 //---* Done *---------------------------------------------------------- 355 return this; 356 } // setModality() 357 358 /** 359 * <p>{@summary Sets the 'OnCloseRequest' event handler for the new 360 * alert.}</p> 361 * <p>This operation can be called repeatedly; each consecutive call will 362 * overwrite the value set by the previous one.</p> 363 * 364 * @param eventHandler The event handler 365 * @return The builder reference. 366 * @throws IllegalStateException The method 367 * {@link #build()} 368 * was already called on this builder instance. 369 * 370 * @see Alert#setOnCloseRequest(EventHandler) 371 */ 372 public final AlertBuilder setOnCloseRequest( final EventHandler<DialogEvent> eventHandler ) throws IllegalStateException 373 { 374 checkIsBuilt(); 375 m_Alert.setOnCloseRequest( eventHandler ); 376 377 //---* Done *---------------------------------------------------------- 378 return this; 379 } // setOnCloseRequest() 380 381 /** 382 * <p>{@summary Sets the 'OnHidden' event handler for the new alert.}</p> 383 * <p>This operation can be called repeatedly; each consecutive call will 384 * overwrite the value set by the previous one.</p> 385 * 386 * @param eventHandler The event handler 387 * @return The builder reference. 388 * @throws IllegalStateException The method 389 * {@link #build()} 390 * was already called on this builder instance. 391 * 392 * @see Alert#setOnHidden(EventHandler) 393 */ 394 public final AlertBuilder setOnHidden( final EventHandler<DialogEvent> eventHandler ) throws IllegalStateException 395 { 396 checkIsBuilt(); 397 m_Alert.setOnHidden( eventHandler ); 398 399 //---* Done *---------------------------------------------------------- 400 return this; 401 } // setHidden()} 402 403 /** 404 * <p>{@summary Sets the 'OnHiding' event handler for the new alert.}</p> 405 * <p>This operation can be called repeatedly; each consecutive call will 406 * overwrite the value set by the previous one.</p> 407 * 408 * @param eventHandler The event handler 409 * @return The builder reference. 410 * @throws IllegalStateException The method 411 * {@link #build()} 412 * was already called on this builder instance. 413 * 414 * @see Alert#setOnHiding(EventHandler) 415 */ 416 public final AlertBuilder setOnHiding( final EventHandler<DialogEvent> eventHandler ) throws IllegalStateException 417 { 418 checkIsBuilt(); 419 m_Alert.setOnHiding( eventHandler ); 420 421 //---* Done *---------------------------------------------------------- 422 return this; 423 } // setHiding()} 424 425 /** 426 * <p>{@summary Sets the 'OnShowing' event handler for the new alert.}</p> 427 * <p>This operation can be called repeatedly; each consecutive call will 428 * overwrite the value set by the previous one.</p> 429 * 430 * @param eventHandler The event handler 431 * @return The builder reference. 432 * @throws IllegalStateException The method 433 * {@link #build()} 434 * was already called on this builder instance. 435 * 436 * @see Alert#setOnShowing(EventHandler) 437 */ 438 public final AlertBuilder setOnShowing( final EventHandler<DialogEvent> eventHandler ) throws IllegalStateException 439 { 440 checkIsBuilt(); 441 m_Alert.setOnShowing( eventHandler ); 442 443 //---* Done *---------------------------------------------------------- 444 return this; 445 } // setOnShowing() 446 447 /** 448 * <p>{@summary Sets the 'OnShown' event handler for the new alert.}</p> 449 * <p>This operation can be called repeatedly; each consecutive call will 450 * overwrite the value set by the previous one.</p> 451 * 452 * @param eventHandler The event handler 453 * @return The builder reference. 454 * @throws IllegalStateException The method 455 * {@link #build()} 456 * was already called on this builder instance. 457 * 458 * @see Alert#setOnShown(EventHandler) 459 */ 460 public final AlertBuilder setOnShown( final EventHandler<DialogEvent> eventHandler ) throws IllegalStateException 461 { 462 checkIsBuilt(); 463 m_Alert.setOnShown( eventHandler ); 464 465 //---* Done *---------------------------------------------------------- 466 return this; 467 } // setOnShown() 468 469 /** 470 * Sets the parent window for the new alert. 471 * 472 * @param owner The parent window. 473 * @return The builder reference. 474 * @throws IllegalStateException The method 475 * {@link #build()} 476 * was already called on this builder instance. 477 * 478 * @see Alert#initOwner(Window) 479 * 480 * @since 0.4.2 481 */ 482 @API( status = STABLE, since = "0.4.1" ) 483 @SuppressWarnings( "UnusedReturnValue" ) 484 public final AlertBuilder setOwner( final Window owner ) throws IllegalStateException 485 { 486 checkIsBuilt(); 487 m_Alert.initOwner( owner ); 488 489 //---* Done *---------------------------------------------------------- 490 return this; 491 } // setOwner() 492 493 /** 494 * <p>{@summary Sets the position for the new alert.}</p> 495 * <p>This operation can be called repeatedly; each consecutive call will 496 * overwrite the values set by the previous one.</p> 497 * 498 * @param x The x position for the alert window. 499 * @param y The y position for the alert window. 500 * @return The builder reference. 501 * @throws IllegalStateException The method 502 * {@link #build()} 503 * was already called on this builder instance. 504 * 505 * @see Alert#setX(double) 506 * @see Alert#setY(double) 507 */ 508 public final AlertBuilder setPos( final double x, final double y ) throws IllegalStateException 509 { 510 setX( x ); 511 setY( y ); 512 513 //---* Done *---------------------------------------------------------- 514 return this; 515 } // setPos() 516 517 /** 518 * <p>{@summary Sets the flag that indicates whether the window for the 519 * new alert can be resized.}</p> 520 * <p>This operation can be called repeatedly; each consecutive call will 521 * overwrite the value set by the previous one.</p> 522 * 523 * @param flag {@code true} for a resizeable window, _false for a 524 * window with fixed sizes. 525 * @return The builder reference. 526 * @throws IllegalStateException The method 527 * {@link #build()} 528 * was already called on this builder instance. 529 * 530 * @see Alert#setResizable(boolean) 531 */ 532 public final AlertBuilder setResizable( final boolean flag ) throws IllegalStateException 533 { 534 checkIsBuilt(); 535 m_Alert.setResizable( flag ); 536 537 //---* Done *---------------------------------------------------------- 538 return this; 539 } // setResizable() 540 541 /** 542 * <p>{@summary Sets the result converter for the new alert.}</p> 543 * <p>This operation can be called repeatedly; each consecutive call will 544 * overwrite the value set by the previous one.</p> 545 * <p>Setting a result converter may have an impact on the behaviour of 546 * {@link #execute()}.</p> 547 * 548 * @param callback The result converter; can be {@code null}. 549 * @return The builder reference. 550 * @throws IllegalStateException The method 551 * {@link #build()} 552 * was already called on this builder instance. 553 * 554 * @see Alert#setResultConverter(Callback) 555 */ 556 public final AlertBuilder setResultConverter( final Callback<ButtonType,ButtonType> callback ) throws IllegalStateException 557 { 558 checkIsBuilt(); 559 m_Alert.setResultConverter( callback ); 560 561 //---* Done *---------------------------------------------------------- 562 return this; 563 } // setResultConverter() 564 565 /** 566 * <p>{@summary Sets the 567 * {@linkplain StageStyle style} 568 * for the new alert.}</p> 569 * 570 * @param style The style. 571 * @return The builder reference. 572 * @throws IllegalStateException The method 573 * {@link #build()} 574 * was already called on this builder instance. 575 * 576 * @see Alert#initStyle(StageStyle) 577 */ 578 public final AlertBuilder setStyle( final StageStyle style ) throws IllegalStateException 579 { 580 checkIsBuilt(); 581 m_Alert.initStyle( requireNonNullArgument( style, "style" ) ); 582 583 //---* Done *---------------------------------------------------------- 584 return this; 585 } // setStyle() 586 587 /** 588 * <p>{@summary Sets the window title for the new alert.}</p> 589 * <p>This operation can be called repeatedly; each consecutive call will 590 * overwrite the value set by the previous one.</p> 591 * 592 * @param s The window title; can be {@code null}. 593 * @return The builder reference. 594 * @throws IllegalStateException The method 595 * {@link #build()} 596 * was already called on this builder instance. 597 * 598 * @see Alert#setTitle(String) 599 */ 600 public final AlertBuilder setTitle( final String s ) throws IllegalStateException 601 { 602 checkIsBuilt(); 603 m_Alert.setTitle( s ); 604 605 //---* Done *---------------------------------------------------------- 606 return this; 607 } // setTitle() 608 609 /** 610 * <p>{@summary Sets the window width for the new alert.}</p> 611 * <p>This operation can be called repeatedly; each consecutive call will 612 * overwrite the value set by the previous one.</p> 613 * 614 * @param width The window width. 615 * @return The builder reference. 616 * @throws IllegalStateException The method 617 * {@link #build()} 618 * was already called on this builder instance. 619 * 620 * @see Alert#setWidth(double) 621 */ 622 @SuppressWarnings( "UnusedReturnValue" ) 623 public final AlertBuilder setWidth( final double width ) throws IllegalStateException 624 { 625 checkIsBuilt(); 626 m_Alert.setWidth( width ); 627 628 //---* Done *---------------------------------------------------------- 629 return this; 630 } // setWidth() 631 632 /** 633 * <p>{@summary Sets the x position for the new alert.}</p> 634 * <p>This operation can be called repeatedly; each consecutive call will 635 * overwrite the value set by the previous one.</p> 636 * 637 * @param x The x position. 638 * @return The builder reference. 639 * @throws IllegalStateException The method 640 * {@link #build()} 641 * was already called on this builder instance. 642 * 643 * @see Alert#setX(double) 644 */ 645 @SuppressWarnings( "UnusedReturnValue" ) 646 public final AlertBuilder setX( final double x ) throws IllegalStateException 647 { 648 checkIsBuilt(); 649 m_Alert.setX( x ); 650 651 //---* Done *---------------------------------------------------------- 652 return this; 653 } // setX() 654 655 /** 656 * <p>{@summary Sets the y position for the new alert.}</p> 657 * <p>This operation can be called repeatedly; each consecutive call will 658 * overwrite the value set by the previous one.</p> 659 * 660 * @param y The y position. 661 * @return The builder reference. 662 * @throws IllegalStateException The method 663 * {@link #build()} 664 * was already called on this builder instance. 665 * 666 * @see Alert#setX(double) 667 */ 668 @SuppressWarnings( "UnusedReturnValue" ) 669 public final AlertBuilder setY( final double y ) throws IllegalStateException 670 { 671 checkIsBuilt(); 672 m_Alert.setY( y ); 673 674 //---* Done *---------------------------------------------------------- 675 return this; 676 } // setY() 677} 678// class AlertBuilder 679 680/* 681 * End of File 682 */