├── README ├── code └── name │ └── antonsmirnov │ └── javafx │ └── dialog │ ├── Dialog.java │ ├── bugIcon.png │ ├── confirmationIcon.png │ ├── errorIcon.png │ ├── infoIcon.png │ └── warningIcon.png ├── resources ├── dialog.properties └── dialog_fr.properties ├── scr ├── confirm.png ├── error.png ├── minwidth.png ├── multiline.png ├── throwable1.png ├── throwable2.png └── wrap.png └── test └── name └── antonsmirnov └── javafx └── dialog └── LocaleDemo.java /README: -------------------------------------------------------------------------------- 1 | JavaFxDialog 2 | Standard dialogs for JavaFX 2 3 | 4 | https://github.com/4ntoine/JavaFxDialog 5 | 6 | Please visit the [Wiki](https://github.com/4ntoine/JavaFxDialog/wiki) for samples and documentation. -------------------------------------------------------------------------------- /code/name/antonsmirnov/javafx/dialog/Dialog.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.javafx.dialog; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.ResourceBundle; 8 | 9 | import javafx.beans.value.ChangeListener; 10 | import javafx.beans.value.ObservableValue; 11 | import javafx.event.ActionEvent; 12 | import javafx.event.EventHandler; 13 | import javafx.geometry.Insets; 14 | import javafx.geometry.Pos; 15 | import javafx.scene.Scene; 16 | import javafx.scene.control.Button; 17 | import javafx.scene.control.Label; 18 | import javafx.scene.control.ScrollPane; 19 | import javafx.scene.control.ToggleButton; 20 | import javafx.scene.image.Image; 21 | import javafx.scene.image.ImageView; 22 | import javafx.scene.input.Clipboard; 23 | import javafx.scene.input.DataFormat; 24 | import javafx.scene.layout.BorderPane; 25 | import javafx.scene.layout.HBox; 26 | import javafx.scene.layout.VBox; 27 | import javafx.stage.Modality; 28 | import javafx.stage.Stage; 29 | import javafx.stage.StageStyle; 30 | import javafx.stage.Window; 31 | 32 | /** 33 | * 34 | * @author Anton Smirnov (dev@antonsmirnov.name) 35 | */ 36 | public class Dialog extends Stage { 37 | 38 | protected String stacktrace; 39 | protected double originalWidth, originalHeight; 40 | 41 | protected Scene scene; 42 | protected BorderPane borderPanel; 43 | protected ImageView icon; 44 | 45 | protected VBox messageBox; 46 | protected Label messageLabel; 47 | 48 | protected boolean stacktraceVisible; 49 | protected HBox stacktraceButtonsPanel; 50 | protected ToggleButton viewStacktraceButton; 51 | protected Button copyStacktraceButton; 52 | protected ScrollPane scrollPane; 53 | protected Label stackTraceLabel; 54 | 55 | protected HBox buttonsPanel; 56 | protected Button okButton; 57 | 58 | /** 59 | * Extracts stack trace from Throwable 60 | */ 61 | protected static class StacktraceExtractor { 62 | 63 | public String extract(Throwable t) { 64 | StringWriter sw = new StringWriter(); 65 | PrintWriter pw = new PrintWriter(sw); 66 | t.printStackTrace(pw); 67 | return sw.toString(); 68 | } 69 | } 70 | 71 | /** 72 | * Dialog builder 73 | */ 74 | public static class Builder { 75 | protected static final int STACKTRACE_LABEL_MAXHEIGHT = 240; 76 | protected static final int MESSAGE_MIN_WIDTH = 180; 77 | protected static final int MESSAGE_MAX_WIDTH = 800; 78 | protected static final int BUTTON_WIDTH = 60; 79 | protected static final double MARGIN = 10; 80 | protected static final String ICON_PATH = "/name/antonsmirnov/javafx/dialog/"; 81 | 82 | protected Dialog stage; 83 | private ResourceBundle resources; 84 | 85 | public Builder create() { 86 | resources = ResourceBundle.getBundle("dialog"); 87 | stage = new Dialog(); 88 | stage.setResizable(false); 89 | stage.initStyle(StageStyle.UTILITY); 90 | stage.initModality(Modality.APPLICATION_MODAL); 91 | stage.setIconified(false); 92 | stage.centerOnScreen(); 93 | stage.borderPanel = new BorderPane(); 94 | 95 | // icon 96 | stage.icon = new ImageView(); 97 | stage.borderPanel.setLeft(stage.icon); 98 | BorderPane.setMargin(stage.icon, new Insets(MARGIN)); 99 | 100 | // message 101 | stage.messageBox = new VBox(); 102 | stage.messageBox.setAlignment(Pos.CENTER_LEFT); 103 | 104 | stage.messageLabel = new Label(); 105 | stage.messageLabel.setWrapText(true); 106 | stage.messageLabel.setMinWidth(MESSAGE_MIN_WIDTH); 107 | stage.messageLabel.setMaxWidth(MESSAGE_MAX_WIDTH); 108 | 109 | stage.messageBox.getChildren().add(stage.messageLabel); 110 | stage.borderPanel.setCenter(stage.messageBox); 111 | BorderPane.setAlignment(stage.messageBox, Pos.CENTER); 112 | BorderPane.setMargin(stage.messageBox, new Insets(MARGIN, MARGIN, MARGIN, 2 * MARGIN)); 113 | 114 | // buttons 115 | stage.buttonsPanel = new HBox(); 116 | stage.buttonsPanel.setSpacing(MARGIN); 117 | stage.buttonsPanel.setAlignment(Pos.BOTTOM_CENTER); 118 | BorderPane.setMargin(stage.buttonsPanel, new Insets(0, 0, 1.5 * MARGIN, 0)); 119 | stage.borderPanel.setBottom(stage.buttonsPanel); 120 | stage.borderPanel.widthProperty().addListener(new ChangeListener () { 121 | 122 | public void changed(ObservableValue ov, Number t, Number t1) { 123 | stage.buttonsPanel.layout(); 124 | } 125 | 126 | }); 127 | 128 | stage.scene = new Scene(stage.borderPanel); 129 | stage.setScene(stage.scene); 130 | return this; 131 | } 132 | 133 | public Builder setModality(Modality modality) { 134 | if (modality != null) { 135 | stage.initModality(modality); 136 | } 137 | return this; 138 | } 139 | 140 | public Builder setOwner(Window owner) { 141 | if (owner != null) { 142 | stage.initOwner(owner); 143 | stage.borderPanel.setMaxWidth(owner.getWidth()); 144 | stage.borderPanel.setMaxHeight(owner.getHeight()); 145 | } 146 | return this; 147 | } 148 | 149 | public Builder setTitle(String title) { 150 | stage.setTitle(title); 151 | return this; 152 | } 153 | 154 | public Builder setMessage(String message) { 155 | stage.messageLabel.setText(message); 156 | return this; 157 | } 158 | 159 | private void alignScrollPane() { 160 | stage.setWidth(stage.icon.getImage().getWidth() + Math.max(stage.messageLabel.getWidth(), 161 | (stage.stacktraceVisible ? Math.max(stage.stacktraceButtonsPanel.getWidth(), 162 | stage.stackTraceLabel.getWidth()) : stage.stacktraceButtonsPanel.getWidth())) + 5 * MARGIN); 163 | 164 | stage.setHeight( 165 | Math.max( 166 | stage.icon.getImage().getHeight(), 167 | stage.messageLabel.getHeight() 168 | + stage.stacktraceButtonsPanel.getHeight() 169 | + (stage.stacktraceVisible 170 | ? Math.min( 171 | stage.stackTraceLabel.getHeight(), 172 | STACKTRACE_LABEL_MAXHEIGHT) 173 | : 0)) 174 | 175 | + stage.buttonsPanel.getHeight() 176 | + 3 * MARGIN); 177 | if (stage.stacktraceVisible) { 178 | stage.scrollPane.setPrefHeight( 179 | stage.getHeight() 180 | - stage.messageLabel.getHeight() 181 | - stage.stacktraceButtonsPanel.getHeight() 182 | - 2 * MARGIN); 183 | } 184 | 185 | stage.centerOnScreen(); 186 | } 187 | 188 | // NOTE: invoke once during Dialog creating 189 | private Builder setStackTrace(Throwable t) { 190 | // view button 191 | stage.viewStacktraceButton = new ToggleButton(getString("buttonlabel.viewstacktrace")); 192 | 193 | // copy button 194 | stage.copyStacktraceButton = new Button(getString("buttonlabel.copystacktrace")); 195 | HBox.setMargin(stage.copyStacktraceButton, new Insets(0, 0, 0, MARGIN)); 196 | 197 | stage.stacktraceButtonsPanel = new HBox(); 198 | stage.stacktraceButtonsPanel.getChildren().addAll( 199 | stage.viewStacktraceButton, stage.copyStacktraceButton); 200 | VBox.setMargin(stage.stacktraceButtonsPanel, new Insets(MARGIN, MARGIN, MARGIN, 0)); 201 | stage.messageBox.getChildren().add(stage.stacktraceButtonsPanel); 202 | 203 | // stacktrace text 204 | stage.stackTraceLabel = new Label(); 205 | stage.stackTraceLabel.widthProperty().addListener(new ChangeListener() { 206 | 207 | public void changed(ObservableValue ov, Number oldValue, Number newValue) { 208 | alignScrollPane(); 209 | } 210 | }); 211 | 212 | stage.stackTraceLabel.heightProperty().addListener(new ChangeListener() { 213 | 214 | public void changed(ObservableValue ov, Number oldValue, Number newValue) { 215 | alignScrollPane(); 216 | } 217 | }); 218 | 219 | StacktraceExtractor extractor = new StacktraceExtractor(); 220 | stage.stacktrace = extractor.extract(t); 221 | 222 | stage.scrollPane = new ScrollPane(); 223 | stage.scrollPane.setContent(stage.stackTraceLabel); 224 | 225 | stage.viewStacktraceButton.setOnAction(new EventHandler() { 226 | 227 | public void handle(ActionEvent t) { 228 | stage.stacktraceVisible = !stage.stacktraceVisible; 229 | if (stage.stacktraceVisible) { 230 | stage.messageBox.getChildren().add(stage.scrollPane); 231 | stage.stackTraceLabel.setText(stage.stacktrace); 232 | 233 | alignScrollPane(); 234 | } else { 235 | stage.messageBox.getChildren().remove(stage.scrollPane); 236 | 237 | //alignScrollPane(); 238 | stage.setWidth(stage.originalWidth); 239 | stage.setHeight(stage.originalHeight); 240 | stage.stackTraceLabel.setText(null); 241 | stage.centerOnScreen(); 242 | } 243 | stage.messageBox.layout(); 244 | } 245 | }); 246 | 247 | stage.copyStacktraceButton.setOnAction(new EventHandler() { 248 | 249 | public void handle(ActionEvent t) { 250 | Clipboard clipboard = Clipboard.getSystemClipboard(); 251 | Map map = new HashMap(); 252 | map.put(DataFormat.PLAIN_TEXT, stage.stacktrace); 253 | clipboard.setContent(map); 254 | } 255 | }); 256 | 257 | stage.showingProperty().addListener(new ChangeListener() { 258 | 259 | public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) { 260 | if (newValue) { 261 | stage.originalWidth = stage.getWidth(); 262 | stage.originalHeight = stage.getHeight(); 263 | } 264 | } 265 | }); 266 | 267 | return this; 268 | } 269 | 270 | protected void setIconFromResource(String resourceName) { 271 | final Image image = new Image(getClass().getResourceAsStream(resourceName)); 272 | stage.icon.setImage(image); 273 | } 274 | 275 | protected Builder setWarningIcon() { 276 | setIconFromResource(ICON_PATH + "warningIcon.png"); 277 | return this; 278 | } 279 | 280 | protected Builder setErrorIcon() { 281 | setIconFromResource(ICON_PATH + "errorIcon.png"); 282 | return this; 283 | } 284 | 285 | protected Builder setThrowableIcon() { 286 | setIconFromResource(ICON_PATH + "bugIcon.png"); 287 | return this; 288 | } 289 | 290 | protected Builder setInfoIcon() { 291 | setIconFromResource(ICON_PATH + "infoIcon.png"); 292 | return this; 293 | } 294 | 295 | protected Builder setConfirmationIcon() { 296 | setIconFromResource(ICON_PATH + "confirmationIcon.png"); 297 | return this; 298 | } 299 | 300 | protected Builder addOkButton() { 301 | stage.okButton = new Button(getString("buttonlabel.ok")); 302 | stage.okButton.setPrefWidth(BUTTON_WIDTH); 303 | stage.okButton.setOnAction(new EventHandler () { 304 | 305 | public void handle(ActionEvent t) { 306 | stage.close(); 307 | } 308 | 309 | }); 310 | stage.buttonsPanel.getChildren().add(stage.okButton); 311 | return this; 312 | } 313 | 314 | public Builder addConfirmationButton(String buttonCaption, final EventHandler actionHandler) { 315 | Button confirmationButton = new Button(buttonCaption); 316 | confirmationButton.setMinWidth(BUTTON_WIDTH); 317 | confirmationButton.setOnAction(new EventHandler() { 318 | 319 | public void handle(ActionEvent t) { 320 | stage.close(); 321 | if (actionHandler != null) 322 | actionHandler.handle(t); 323 | } 324 | }); 325 | 326 | stage.buttonsPanel.getChildren().add(confirmationButton); 327 | return this; 328 | } 329 | 330 | /** 331 | * Add Yes button to confirmation dialog 332 | * 333 | * @param actionHandler action handler 334 | * @return 335 | */ 336 | public Builder addYesButton(EventHandler actionHandler) { 337 | return addConfirmationButton(getString("buttonlabel.yes"), actionHandler); 338 | } 339 | 340 | /** 341 | * Add No button to confirmation dialog 342 | * 343 | * @param actionHandler action handler 344 | * @return 345 | */ 346 | public Builder addNoButton(EventHandler actionHandler) { 347 | return addConfirmationButton(getString("buttonlabel.no"), actionHandler); 348 | } 349 | 350 | /** 351 | * Add Cancel button to confirmation dialog 352 | * 353 | * @param actionHandler action handler 354 | * @return 355 | */ 356 | public Builder addCancelButton(EventHandler actionHandler) { 357 | return addConfirmationButton(getString("buttonlabel.cancel"), actionHandler); 358 | } 359 | 360 | /** 361 | * Build dialog 362 | * 363 | * @return dialog instance 364 | */ 365 | public Dialog build() { 366 | if (stage.buttonsPanel.getChildren().size() == 0) 367 | throw new RuntimeException("Add one dialog button at least"); 368 | 369 | stage.buttonsPanel.getChildren().get(0).requestFocus(); 370 | return stage; 371 | } 372 | 373 | 374 | private String getString(String key) { 375 | return resources.getString(key); 376 | } 377 | } 378 | 379 | /** 380 | * Show information dialog box as parentWindow child 381 | * 382 | * @param title dialog title 383 | * @param message dialog message 384 | * @param owner parent window 385 | */ 386 | public static void showInfo(String title, String message, Window owner) { 387 | new Builder() 388 | .create() 389 | .setOwner(owner) 390 | .setTitle(title) 391 | .setInfoIcon() 392 | .setMessage(message) 393 | .addOkButton() 394 | .build() 395 | .show(); 396 | } 397 | 398 | /** 399 | * Show information dialog box as parentStage child 400 | * 401 | * @param title dialog title 402 | * @param message dialog message 403 | */ 404 | public static void showInfo(String title, String message) { 405 | showInfo(title, message, null); 406 | } 407 | 408 | /** 409 | * Show warning dialog box as parentStage child 410 | * 411 | * @param title dialog title 412 | * @param message dialog message 413 | * @param owner parent window 414 | */ 415 | public static void showWarning(String title, String message, Window owner) { 416 | new Builder() 417 | .create() 418 | .setOwner(owner) 419 | .setTitle(title) 420 | .setWarningIcon() 421 | .setMessage(message) 422 | .addOkButton() 423 | .build() 424 | .show(); 425 | } 426 | 427 | /** 428 | * Show warning dialog box 429 | * 430 | * @param title dialog title 431 | * @param message dialog message 432 | */ 433 | public static void showWarning(String title, String message) { 434 | showWarning(title, message, null); 435 | } 436 | 437 | /** 438 | * Show error dialog box 439 | * 440 | * @param title dialog title 441 | * @param message dialog message 442 | * @param owner parent window 443 | */ 444 | public static void showError(String title, String message, Window owner) { 445 | new Builder() 446 | .create() 447 | .setOwner(owner) 448 | .setTitle(title) 449 | .setErrorIcon() 450 | .setMessage(message) 451 | .addOkButton() 452 | .build() 453 | .show(); 454 | } 455 | 456 | /** 457 | * Show error dialog box 458 | * 459 | * @param title dialog title 460 | * @param message dialog message 461 | */ 462 | public static void showError(String title, String message) { 463 | showError(title, message, null); 464 | } 465 | 466 | /** 467 | * Show error dialog box with stacktrace 468 | * 469 | * @param title dialog title 470 | * @param message dialog message 471 | * @param t throwable 472 | * @param owner parent window 473 | */ 474 | public static void showThrowable(String title, String message, Throwable t, Window owner) { 475 | new Builder() 476 | .create() 477 | .setOwner(owner) 478 | .setTitle(title) 479 | .setThrowableIcon() 480 | .setMessage(message) 481 | .setStackTrace(t) 482 | .addOkButton() 483 | .build() 484 | .show(); 485 | } 486 | 487 | /** 488 | * Show error dialog box with stacktrace 489 | * 490 | * @param title dialog title 491 | * @param message dialog message 492 | * @param t throwable 493 | */ 494 | public static void showThrowable(String title, String message, Throwable t) { 495 | showThrowable(title, message, t, null); 496 | } 497 | 498 | /** 499 | * Build confirmation dialog builder 500 | * 501 | * @param title dialog title 502 | * @param message dialog message 503 | * @param owner parent window 504 | * @return 505 | */ 506 | public static Builder buildConfirmation(String title, String message, Window owner) { 507 | return new Builder() 508 | .create() 509 | .setOwner(owner) 510 | .setTitle(title) 511 | .setConfirmationIcon() 512 | .setMessage(message); 513 | } 514 | 515 | /** 516 | * Build confirmation dialog builder 517 | * 518 | * @param title dialog title 519 | * @param message dialog message 520 | * @return 521 | */ 522 | public static Builder buildConfirmation(String title, String message) { 523 | return buildConfirmation(title, message, null); 524 | } 525 | } 526 | -------------------------------------------------------------------------------- /code/name/antonsmirnov/javafx/dialog/bugIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/code/name/antonsmirnov/javafx/dialog/bugIcon.png -------------------------------------------------------------------------------- /code/name/antonsmirnov/javafx/dialog/confirmationIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/code/name/antonsmirnov/javafx/dialog/confirmationIcon.png -------------------------------------------------------------------------------- /code/name/antonsmirnov/javafx/dialog/errorIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/code/name/antonsmirnov/javafx/dialog/errorIcon.png -------------------------------------------------------------------------------- /code/name/antonsmirnov/javafx/dialog/infoIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/code/name/antonsmirnov/javafx/dialog/infoIcon.png -------------------------------------------------------------------------------- /code/name/antonsmirnov/javafx/dialog/warningIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/code/name/antonsmirnov/javafx/dialog/warningIcon.png -------------------------------------------------------------------------------- /resources/dialog.properties: -------------------------------------------------------------------------------- 1 | buttonlabel.ok = OK 2 | buttonlabel.yes = Yes 3 | buttonlabel.no = No 4 | buttonlabel.cancel = Cancel 5 | buttonlabel.viewstacktrace = View stacktrace 6 | buttonlabel.copystacktrace = Copy to clipboard -------------------------------------------------------------------------------- /resources/dialog_fr.properties: -------------------------------------------------------------------------------- 1 | buttonlabel.ok=OK 2 | buttonlabel.yes=Oui 3 | buttonlabel.no=Non 4 | buttonlabel.cancel=Annuler\ 5 | buttonlabel.viewstacktrace=Consulter l'erreur (stacktrace) 6 | buttonlabel.copystacktrace=Copier le message d'erreur -------------------------------------------------------------------------------- /scr/confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/scr/confirm.png -------------------------------------------------------------------------------- /scr/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/scr/error.png -------------------------------------------------------------------------------- /scr/minwidth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/scr/minwidth.png -------------------------------------------------------------------------------- /scr/multiline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/scr/multiline.png -------------------------------------------------------------------------------- /scr/throwable1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/scr/throwable1.png -------------------------------------------------------------------------------- /scr/throwable2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/scr/throwable2.png -------------------------------------------------------------------------------- /scr/wrap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ntoine/JavaFxDialog/b016182025757f81002a046cbff4a69b6f952dae/scr/wrap.png -------------------------------------------------------------------------------- /test/name/antonsmirnov/javafx/dialog/LocaleDemo.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.javafx.dialog; 2 | 3 | import javafx.application.Application; 4 | import javafx.event.Event; 5 | import javafx.event.EventHandler; 6 | import javafx.stage.Stage; 7 | 8 | import java.util.Locale; 9 | 10 | public class LocaleDemo extends Application { 11 | public static void main(String[] args) { 12 | launch(args); 13 | } 14 | 15 | @Override 16 | public void start(Stage stage) throws Exception { 17 | Locale.setDefault(Locale.FRENCH); 18 | Dialog.buildConfirmation("", "").addYesButton(new EventHandler() { 19 | @Override 20 | public void handle(Event event) { 21 | //nothing to do 22 | } 23 | }).build().show(); 24 | } 25 | } 26 | --------------------------------------------------------------------------------