Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.stage; 027 028import java.security.AllPermission; 029import java.security.AccessControlContext; 030import java.security.AccessController; 031import java.util.Iterator; 032 033import javafx.beans.property.DoubleProperty; 034import javafx.beans.property.DoublePropertyBase; 035import javafx.beans.property.ObjectProperty; 036import javafx.beans.property.ObjectPropertyBase; 037import javafx.beans.property.ReadOnlyBooleanProperty; 038import javafx.beans.property.ReadOnlyBooleanWrapper; 039import javafx.beans.property.ReadOnlyObjectProperty; 040import javafx.beans.property.ReadOnlyObjectWrapper; 041import javafx.beans.property.ReadOnlyDoubleProperty; 042import javafx.beans.property.ReadOnlyDoubleWrapper; 043import javafx.beans.property.SimpleObjectProperty; 044import javafx.event.Event; 045import javafx.event.EventDispatchChain; 046import javafx.event.EventDispatcher; 047import javafx.event.EventHandler; 048import javafx.event.EventTarget; 049import javafx.event.EventType; 050import javafx.geometry.Rectangle2D; 051import javafx.scene.Scene; 052 053import com.sun.javafx.Utils; 054import com.sun.javafx.WeakReferenceQueue; 055import com.sun.javafx.css.StyleManager; 056import com.sun.javafx.stage.WindowEventDispatcher; 057import com.sun.javafx.stage.WindowHelper; 058import com.sun.javafx.stage.WindowPeerListener; 059import com.sun.javafx.tk.TKPulseListener; 060import com.sun.javafx.tk.TKScene; 061import com.sun.javafx.tk.TKStage; 062import com.sun.javafx.tk.Toolkit; 063 064 065/** 066 * <p> 067 * A top level window within which a scene is hosted, and with which the user 068 * interacts. A Window might be a {@link Stage}, {@link PopupWindow}, or other 069 * such top level. A Window is used also for browser plug-in based deployments. 070 * </p> 071 * 072 */ 073public class Window implements EventTarget { 074 075 /** 076 * A list of all the currently existing windows. This is only used by SQE for testing. 077 */ 078 private static WeakReferenceQueue<Window>windowQueue = new WeakReferenceQueue<Window>(); 079 080 static { 081 WindowHelper.setWindowAccessor( 082 new WindowHelper.WindowAccessor() { 083 /** 084 * Allow window peer listeners to directly change window 085 * location and size without changing the xExplicit, 086 * yExplicit, widthExplicit and heightExplicit values. 087 */ 088 @Override 089 public void setLocation(Window window, double x, double y) { 090 window.x.set(x - window.winTranslateX); 091 window.y.set(y - window.winTranslateY); 092 } 093 094 @Override 095 public void setSize(Window window, 096 double width, 097 double height) { 098 window.width.set(width); 099 window.height.set(height); 100 } 101 }); 102 } 103 104 /** 105 * Return all Windows 106 * 107 * @return Iterator of all Windows 108 * @treatAsPrivate implementation detail 109 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 110 */ 111 @Deprecated 112 public static Iterator<Window> impl_getWindows() { 113 final SecurityManager securityManager = System.getSecurityManager(); 114 if (securityManager != null) { 115 securityManager.checkPermission(new AllPermission()); 116 } 117 118 return (Iterator<Window>) windowQueue.iterator(); 119 } 120 121 private final AccessControlContext acc = AccessController.getContext(); 122 123 protected Window() { 124 // necessary for WindowCloseRequestHandler 125 initializeInternalEventDispatcher(); 126 } 127 128 /** 129 * The listener that gets called by peer. It's also responsible for 130 * window size/location synchronization with the window peer, which 131 * occurs on every pulse. 132 * 133 * @treatAsPrivate implementation detail 134 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 135 */ 136 @Deprecated 137 protected WindowPeerListener peerListener; 138 139 /** 140 * The peer of this Stage. All external access should be 141 * made though getPeer(). Implementors note: Please ensure that this 142 * variable is defined *after* style and *before* the other variables so 143 * that style has been initialized prior to this call, and so that 144 * impl_peer is initialized prior to subsequent initialization. 145 * 146 * @treatAsPrivate implementation detail 147 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 148 */ 149 @Deprecated 150 protected TKStage impl_peer; 151 152 private TKBoundsConfigurator peerBoundsConfigurator = 153 new TKBoundsConfigurator(); 154 155 /** 156 * Get Stage's peer 157 * 158 * @treatAsPrivate implementation detail 159 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 160 */ 161 @Deprecated 162 public TKStage impl_getPeer() { 163 return impl_peer; 164 } 165 166 /** 167 * @treatAsPrivate implementation detail 168 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 169 */ 170 @Deprecated 171 public String impl_getMXWindowType() { 172 return getClass().getSimpleName(); 173 } 174 175 /** 176 * Set the width and height of this Window to match the size of the content 177 * of this Window's Scene. 178 */ 179 public void sizeToScene() { 180 if (getScene() != null && impl_peer != null) { 181 getScene().impl_preferredSize(); 182 adjustSize(false); 183 } 184 } 185 186 private void adjustSize(boolean selfSizePriority) { 187 if (getScene() == null) { 188 return; 189 } 190 if (impl_peer != null) { 191 double sceneWidth = getScene().getWidth(); 192 double cw = (sceneWidth > 0) ? sceneWidth : -1; 193 double w = -1; 194 if (selfSizePriority && widthExplicit) { 195 w = getWidth(); 196 } else if (cw <= 0) { 197 w = widthExplicit ? getWidth() : -1; 198 } else { 199 widthExplicit = false; 200 } 201 double sceneHeight = getScene().getHeight(); 202 double ch = (sceneHeight > 0) ? sceneHeight : -1; 203 double h = -1; 204 if (selfSizePriority && heightExplicit) { 205 h = getHeight(); 206 } else if (ch <= 0) { 207 h = heightExplicit ? getHeight() : -1; 208 } else { 209 heightExplicit = false; 210 } 211 212 peerBoundsConfigurator.setSize(w, h, cw, ch); 213 applyBounds(); 214 } 215 } 216 217 private static final float CENTER_ON_SCREEN_X_FRACTION = 1.0f / 2; 218 private static final float CENTER_ON_SCREEN_Y_FRACTION = 1.0f / 3; 219 220 /** 221 * Sets x and y properties on this Window so that it is centered on the screen. 222 */ 223 public void centerOnScreen() { 224 xExplicit = false; 225 yExplicit = false; 226 if (impl_peer != null) { 227 Rectangle2D bounds = getWindowScreen().getVisualBounds(); 228 double centerX = 229 bounds.getMinX() + (bounds.getWidth() - getWidth()) 230 * CENTER_ON_SCREEN_X_FRACTION; 231 double centerY = 232 bounds.getMinY() + (bounds.getHeight() - getHeight()) 233 * CENTER_ON_SCREEN_Y_FRACTION; 234 235 x.set(centerX - winTranslateX); 236 y.set(centerY - winTranslateY); 237 peerBoundsConfigurator.setLocation(centerX, centerY, 238 CENTER_ON_SCREEN_X_FRACTION, 239 CENTER_ON_SCREEN_Y_FRACTION); 240 applyBounds(); 241 } 242 } 243 244 private double winTranslateX; 245 private double winTranslateY; 246 247 final void setWindowTranslate(final double translateX, 248 final double translateY) { 249 if (translateX != winTranslateX) { 250 winTranslateX = translateX; 251 peerBoundsConfigurator.setX(getX() + translateX, 0); 252 } 253 if (translateY != winTranslateY) { 254 winTranslateY = translateY; 255 peerBoundsConfigurator.setY(getY() + translateY, 0); 256 } 257 } 258 259 final double getWindowTranslateX() { 260 return winTranslateX; 261 } 262 263 final double getWindowTranslateY() { 264 return winTranslateY; 265 } 266 267 private boolean xExplicit = false; 268 /** 269 * The horizontal location of this {@code Stage} on the screen. Changing 270 * this attribute will move the {@code Stage} horizontally. Changing this 271 * attribute will not visually affect a {@code Stage} while 272 * {@code fullScreen} is true, but will be honored by the {@code Stage} once 273 * {@code fullScreen} becomes false. 274 */ 275 private ReadOnlyDoubleWrapper x = 276 new ReadOnlyDoubleWrapper(this, "x", Double.NaN); 277 278 public final void setX(double value) { 279 x.set(value); 280 peerBoundsConfigurator.setX(value + winTranslateX, 0); 281 xExplicit = true; 282 } 283 public final double getX() { return x.get(); } 284 public final ReadOnlyDoubleProperty xProperty() { return x.getReadOnlyProperty(); } 285 286 private boolean yExplicit = false; 287 /** 288 * The vertical location of this {@code Stage} on the screen. Changing this 289 * attribute will move the {@code Stage} vertically. Changing this 290 * attribute will not visually affect a {@code Stage} while 291 * {@code fullScreen} is true, but will be honored by the {@code Stage} once 292 * {@code fullScreen} becomes false. 293 */ 294 private ReadOnlyDoubleWrapper y = 295 new ReadOnlyDoubleWrapper(this, "y", Double.NaN); 296 297 public final void setY(double value) { 298 y.set(value); 299 peerBoundsConfigurator.setY(value + winTranslateY, 0); 300 yExplicit = true; 301 } 302 public final double getY() { return y.get(); } 303 public final ReadOnlyDoubleProperty yProperty() { return y.getReadOnlyProperty(); } 304 305 private boolean widthExplicit = false; 306 /** 307 * The width of this {@code Stage}. Changing this attribute will narrow or 308 * widen the width of the {@code Stage}. Changing this 309 * attribute will not visually affect a {@code Stage} while 310 * {@code fullScreen} is true, but will be honored by the {@code Stage} once 311 * {@code fullScreen} becomes false. This value includes any and all 312 * decorations which may be added by the Operating System such as resizable 313 * frame handles. Typical applications will set the {@link javafx.scene.Scene} width 314 * instead. 315 * <p> 316 * The property is read only because it can be changed externally 317 * by the underlying platform and therefore must not be bindable. 318 * </p> 319 */ 320 private ReadOnlyDoubleWrapper width = 321 new ReadOnlyDoubleWrapper(this, "width", Double.NaN); 322 323 public final void setWidth(double value) { 324 width.set(value); 325 peerBoundsConfigurator.setWindowWidth(value); 326 widthExplicit = true; 327 } 328 public final double getWidth() { return width.get(); } 329 public final ReadOnlyDoubleProperty widthProperty() { return width.getReadOnlyProperty(); } 330 331 private boolean heightExplicit = false; 332 /** 333 * The height of this {@code Stage}. Changing this attribute will shrink 334 * or heighten the height of the {@code Stage}. Changing this 335 * attribute will not visually affect a {@code Stage} while 336 * {@code fullScreen} is true, but will be honored by the {@code Stage} once 337 * {@code fullScreen} becomes false. This value includes any and all 338 * decorations which may be added by the Operating System such as the title 339 * bar. Typical applications will set the {@link javafx.scene.Scene} height instead. 340 * <p> 341 * The property is read only because it can be changed externally 342 * by the underlying platform and therefore must not be bindable. 343 * </p> 344 */ 345 private ReadOnlyDoubleWrapper height = 346 new ReadOnlyDoubleWrapper(this, "height", Double.NaN); 347 348 public final void setHeight(double value) { 349 height.set(value); 350 peerBoundsConfigurator.setWindowHeight(value); 351 heightExplicit = true; 352 } 353 public final double getHeight() { return height.get(); } 354 public final ReadOnlyDoubleProperty heightProperty() { return height.getReadOnlyProperty(); } 355 356 /** 357 * Whether or not this {@code Window} has the keyboard or input focus. 358 * <p> 359 * The property is read only because it can be changed externally 360 * by the underlying platform and therefore must not be bindable. 361 * </p> 362 * 363 * @profile common 364 */ 365 private ReadOnlyBooleanWrapper focused = new ReadOnlyBooleanWrapper() { 366 @Override protected void invalidated() { 367 focusChanged(get()); 368 } 369 370 @Override 371 public Object getBean() { 372 return Window.this; 373 } 374 375 @Override 376 public String getName() { 377 return "focused"; 378 } 379 }; 380 381 /** 382 * @treatAsPrivate 383 * @deprecated 384 */ 385 @Deprecated 386 public final void setFocused(boolean value) { focused.set(value); } 387 388 /** 389 * Requests that this {@code Window} get the input focus. 390 */ 391 public final void requestFocus() { 392 if (impl_peer != null) { 393 impl_peer.requestFocus(); 394 } 395 } 396 public final boolean isFocused() { return focused.get(); } 397 public final ReadOnlyBooleanProperty focusedProperty() { return focused.getReadOnlyProperty(); } 398 399 /** 400 * The {@code Scene} to be rendered on this {@code Stage}. There can only 401 * be one {@code Scene} on the {@code Stage} at a time, and a {@code Scene} 402 * can only be on one {@code Stage} at a time. Setting a {@code Scene} on 403 * a different {@code Stage} will cause the old {@code Stage} to lose the 404 * reference before the new one gains it. You may swap {@code Scene}s on 405 * a {@code Stage} at any time, even while in full-screen exclusive mode. 406 * 407 * An {@link IllegalStateException} is thrown if this property is set 408 * on a thread other than the JavaFX Application Thread. 409 * 410 * @defaultValue null 411 */ 412 private SceneModel scene = new SceneModel(); 413 protected void setScene(Scene value) { scene.set(value); } 414 public final Scene getScene() { return scene.get(); } 415 public final ReadOnlyObjectProperty<Scene> sceneProperty() { return scene.getReadOnlyProperty(); } 416 417 private final class SceneModel extends ReadOnlyObjectWrapper<Scene> { 418 private Scene oldScene; 419 420 @Override protected void invalidated() { 421 final Scene newScene = get(); 422 if (oldScene == newScene) { 423 return; 424 } 425 Toolkit.getToolkit().checkFxUserThread(); 426 // First, detach scene peer from this window 427 updatePeerScene(null); 428 // Second, dispose scene peer 429 if (oldScene != null) { 430 oldScene.impl_setWindow(null); 431 StyleManager.getInstance().forget(oldScene); 432 } 433 if (newScene != null) { 434 final Window oldWindow = newScene.getWindow(); 435 if (oldWindow != null) { 436 // if the new scene was previously set to a window 437 // we need to remove it from that window 438 // NOTE: can this "scene" property be bound? 439 oldWindow.setScene(null); 440 } 441 442 // Set the "window" on the new scene. This will also trigger 443 // scene's peer creation. 444 newScene.impl_setWindow(Window.this); 445 // Set scene impl on stage impl 446 updatePeerScene(newScene.impl_getPeer()); 447 448 // Fix for RT-15432: we should update new Scene's stylesheets, if the 449 // window is already showing. For not yet shown windows, the update is 450 // performed in Window.visibleChanging() 451 if (isShowing()) { 452 newScene.getRoot().impl_reapplyCSS(); 453 454 if (!widthExplicit || !heightExplicit) { 455 getScene().impl_preferredSize(); 456 adjustSize(true); 457 } 458 } 459 } 460 461 oldScene = newScene; 462 } 463 464 @Override 465 public Object getBean() { 466 return Window.this; 467 } 468 469 @Override 470 public String getName() { 471 return "scene"; 472 } 473 474 private void updatePeerScene(final TKScene tkScene) { 475 if (impl_peer != null) { 476 // Set scene impl on stage impl 477 impl_peer.setScene(tkScene); 478 } 479 } 480 } 481 482 /** 483 * Defines the opacity of the {@code Stage} as a value between 0.0 and 1.0. 484 * The opacity is reflected across the {@code Stage}, its {@code Decoration} 485 * and its {@code Scene} content. On a JavaFX runtime platform that does not 486 * support opacity, assigning a value to this variable will have no 487 * visible difference. A {@code Stage} with 0% opacity is fully translucent. 488 * Typically, a {@code Stage} with 0% opacity will not receive any mouse 489 * events. 490 * 491 * @defaultValue 1.0 492 */ 493 private DoubleProperty opacity; 494 495 public final void setOpacity(double value) { 496 opacityProperty().set(value); 497 } 498 499 public final double getOpacity() { 500 return opacity == null ? 1.0 : opacity.get(); 501 } 502 503 public final DoubleProperty opacityProperty() { 504 if (opacity == null) { 505 opacity = new DoublePropertyBase(1.0) { 506 507 @Override 508 protected void invalidated() { 509 if (impl_peer != null) { 510 impl_peer.setOpacity((float) get()); 511 } 512 } 513 514 @Override 515 public Object getBean() { 516 return Window.this; 517 } 518 519 @Override 520 public String getName() { 521 return "opacity"; 522 } 523 }; 524 } 525 return opacity; 526 } 527 528 /** 529 * Called when there is an external request to close this {@code Window}. 530 * The installed event handler can prevent window closing by consuming the 531 * received event. 532 */ 533 private ObjectProperty<EventHandler<WindowEvent>> onCloseRequest; 534 public final void setOnCloseRequest(EventHandler<WindowEvent> value) { 535 onCloseRequestProperty().set(value); 536 } 537 public final EventHandler<WindowEvent> getOnCloseRequest() { 538 return (onCloseRequest != null) ? onCloseRequest.get() : null; 539 } 540 public final ObjectProperty<EventHandler<WindowEvent>> 541 onCloseRequestProperty() { 542 if (onCloseRequest == null) { 543 onCloseRequest = new ObjectPropertyBase<EventHandler<WindowEvent>>() { 544 @Override protected void invalidated() { 545 setEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, get()); 546 } 547 548 @Override 549 public Object getBean() { 550 return Window.this; 551 } 552 553 @Override 554 public String getName() { 555 return "onCloseRequest"; 556 } 557 }; 558 } 559 return onCloseRequest; 560 } 561 562 /** 563 * Called just prior to the Window being shown. 564 */ 565 private ObjectProperty<EventHandler<WindowEvent>> onShowing; 566 public final void setOnShowing(EventHandler<WindowEvent> value) { onShowingProperty().set(value); } 567 public final EventHandler<WindowEvent> getOnShowing() { 568 return onShowing == null ? null : onShowing.get(); 569 } 570 public final ObjectProperty<EventHandler<WindowEvent>> onShowingProperty() { 571 if (onShowing == null) { 572 onShowing = new ObjectPropertyBase<EventHandler<WindowEvent>>() { 573 @Override protected void invalidated() { 574 setEventHandler(WindowEvent.WINDOW_SHOWING, get()); 575 } 576 577 @Override 578 public Object getBean() { 579 return Window.this; 580 } 581 582 @Override 583 public String getName() { 584 return "onShowing"; 585 } 586 }; 587 } 588 return onShowing; 589 } 590 591 /** 592 * Called just after the Window is shown. 593 */ 594 private ObjectProperty<EventHandler<WindowEvent>> onShown; 595 public final void setOnShown(EventHandler<WindowEvent> value) { onShownProperty().set(value); } 596 public final EventHandler<WindowEvent> getOnShown() { 597 return onShown == null ? null : onShown.get(); 598 } 599 public final ObjectProperty<EventHandler<WindowEvent>> onShownProperty() { 600 if (onShown == null) { 601 onShown = new ObjectPropertyBase<EventHandler<WindowEvent>>() { 602 @Override protected void invalidated() { 603 setEventHandler(WindowEvent.WINDOW_SHOWN, get()); 604 } 605 606 @Override 607 public Object getBean() { 608 return Window.this; 609 } 610 611 @Override 612 public String getName() { 613 return "onShown"; 614 } 615 }; 616 } 617 return onShown; 618 } 619 620 /** 621 * Called just prior to the Window being hidden. 622 */ 623 private ObjectProperty<EventHandler<WindowEvent>> onHiding; 624 public final void setOnHiding(EventHandler<WindowEvent> value) { onHidingProperty().set(value); } 625 public final EventHandler<WindowEvent> getOnHiding() { 626 return onHiding == null ? null : onHiding.get(); 627 } 628 public final ObjectProperty<EventHandler<WindowEvent>> onHidingProperty() { 629 if (onHiding == null) { 630 onHiding = new ObjectPropertyBase<EventHandler<WindowEvent>>() { 631 @Override protected void invalidated() { 632 setEventHandler(WindowEvent.WINDOW_HIDING, get()); 633 } 634 635 @Override 636 public Object getBean() { 637 return Window.this; 638 } 639 640 @Override 641 public String getName() { 642 return "onHiding"; 643 } 644 }; 645 } 646 return onHiding; 647 } 648 649 /** 650 * Called just after the Window has been hidden. 651 * When the {@code Window} is hidden, this event handler is invoked allowing 652 * the developer to clean up resources or perform other tasks when the 653 * {@link Window} is closed. 654 */ 655 private ObjectProperty<EventHandler<WindowEvent>> onHidden; 656 public final void setOnHidden(EventHandler<WindowEvent> value) { onHiddenProperty().set(value); } 657 public final EventHandler<WindowEvent> getOnHidden() { 658 return onHidden == null ? null : onHidden.get(); 659 } 660 public final ObjectProperty<EventHandler<WindowEvent>> onHiddenProperty() { 661 if (onHidden == null) { 662 onHidden = new ObjectPropertyBase<EventHandler<WindowEvent>>() { 663 @Override protected void invalidated() { 664 setEventHandler(WindowEvent.WINDOW_HIDDEN, get()); 665 } 666 667 @Override 668 public Object getBean() { 669 return Window.this; 670 } 671 672 @Override 673 public String getName() { 674 return "onHidden"; 675 } 676 }; 677 } 678 return onHidden; 679 } 680 681 /** 682 * Whether or not this {@code Stage} is showing (that is, open on the 683 * user's system). The Stage might be "showing", yet the user might not 684 * be able to see it due to the Stage being rendered behind another window 685 * or due to the Stage being positioned off the monitor. 686 * 687 * @defaultValue false 688 */ 689 private ReadOnlyBooleanWrapper showing = new ReadOnlyBooleanWrapper() { 690 private boolean oldVisible; 691 692 @Override protected void invalidated() { 693 final boolean newVisible = get(); 694 if (oldVisible == newVisible) { 695 return; 696 } 697 698 if (!oldVisible && newVisible) { 699 fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_SHOWING)); 700 } else { 701 fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_HIDING)); 702 } 703 704 oldVisible = newVisible; 705 impl_visibleChanging(newVisible); 706 if (newVisible) { 707 hasBeenVisible = true; 708 windowQueue.add(Window.this); 709 } else { 710 windowQueue.remove(Window.this); 711 } 712 Toolkit tk = Toolkit.getToolkit(); 713 if (impl_peer != null) { 714 if (newVisible) { 715 impl_peer.setSecurityContext(acc); 716 717 if (peerListener == null) { 718 peerListener = new WindowPeerListener(Window.this); 719 } 720 721 // Setup listener for changes coming back from peer 722 impl_peer.setTKStageListener(peerListener); 723 // Register pulse listener 724 tk.addStageTkPulseListener(peerBoundsConfigurator); 725 726 if (getScene() != null) { 727 getScene().impl_initPeer(); 728 impl_peer.setScene(getScene().impl_getPeer()); 729 getScene().impl_preferredSize(); 730 } 731 732 // Set peer bounds 733 if ((getScene() != null) && (!widthExplicit || !heightExplicit)) { 734 adjustSize(true); 735 } else { 736 peerBoundsConfigurator.setSize( 737 getWidth(), getHeight(), -1, -1); 738 } 739 740 if (!xExplicit && !yExplicit) { 741 centerOnScreen(); 742 } else { 743 peerBoundsConfigurator.setLocation( 744 getX() + winTranslateX, 745 getY() + winTranslateY, 746 0, 0); 747 } 748 749 // set peer bounds before the window is shown 750 applyBounds(); 751 752 impl_peer.setOpacity((float)getOpacity()); 753 754 impl_peer.setVisible(true); 755 fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_SHOWN)); 756 } else { 757 impl_peer.setVisible(false); 758 759 // Call listener 760 fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_HIDDEN)); 761 762 if (getScene() != null) { 763 impl_peer.setScene(null); 764 getScene().impl_disposePeer(); 765 StyleManager.getInstance().forget(getScene()); 766 } 767 768 // Remove toolkit pulse listener 769 tk.removeStageTkPulseListener(peerBoundsConfigurator); 770 // Remove listener for changes coming back from peer 771 impl_peer.setTKStageListener(null); 772 773 // Notify peer 774 impl_peer.close(); 775 } 776 } 777 if (newVisible) { 778 tk.requestNextPulse(); 779 } 780 impl_visibleChanged(newVisible); 781 } 782 783 @Override 784 public Object getBean() { 785 return Window.this; 786 } 787 788 @Override 789 public String getName() { 790 return "showing"; 791 } 792 }; 793 private void setShowing(boolean value) { 794 Toolkit.getToolkit().checkFxUserThread(); 795 showing.set(value); 796 } 797 public final boolean isShowing() { return showing.get(); } 798 public final ReadOnlyBooleanProperty showingProperty() { return showing.getReadOnlyProperty(); } 799 800 // flag indicating whether this window has ever been made visible. 801 boolean hasBeenVisible = false; 802 803 /** 804 * Attempts to show this Window by setting visibility to true 805 * 806 * @throws IllegalStateException if this method is called on a thread 807 * other than the JavaFX Application Thread. 808 */ 809 protected void show() { 810 setShowing(true); 811 } 812 813 /** 814 * Attempts to hide this Window by setting the visibility to false. 815 * 816 * @throws IllegalStateException if this method is called on a thread 817 * other than the JavaFX Application Thread. 818 */ 819 public void hide() { 820 setShowing(false); 821 } 822 823 /** 824 * This can be replaced by listening for the onShowing/onHiding events 825 * @treatAsPrivate implementation detail 826 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 827 */ 828 @Deprecated 829 protected void impl_visibleChanging(boolean visible) { 830 if (visible && (getScene() != null)) { 831 getScene().getRoot().impl_reapplyCSS(); 832 } 833 } 834 835 /** 836 * This can be replaced by listening for the onShown/onHidden events 837 * @treatAsPrivate implementation detail 838 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 839 */ 840 @Deprecated 841 protected void impl_visibleChanged(boolean visible) { 842 assert impl_peer != null; 843 if (!visible) { 844 peerListener = null; 845 impl_peer = null; 846 } 847 } 848 849 // PENDING_DOC_REVIEW 850 /** 851 * Specifies the event dispatcher for this node. The default event 852 * dispatcher sends the received events to the registered event handlers and 853 * filters. When replacing the value with a new {@code EventDispatcher}, 854 * the new dispatcher should forward events to the replaced dispatcher 855 * to maintain the node's default event handling behavior. 856 */ 857 private ObjectProperty<EventDispatcher> eventDispatcher; 858 859 public final void setEventDispatcher(EventDispatcher value) { 860 eventDispatcherProperty().set(value); 861 } 862 863 public final EventDispatcher getEventDispatcher() { 864 return eventDispatcherProperty().get(); 865 } 866 867 public final ObjectProperty<EventDispatcher> eventDispatcherProperty() { 868 initializeInternalEventDispatcher(); 869 return eventDispatcher; 870 } 871 872 private WindowEventDispatcher internalEventDispatcher; 873 874 // PENDING_DOC_REVIEW 875 /** 876 * Registers an event handler to this node. The handler is called when the 877 * node receives an {@code Event} of the specified type during the bubbling 878 * phase of event delivery. 879 * 880 * @param <T> the specific event class of the handler 881 * @param eventType the type of the events to receive by the handler 882 * @param eventHandler the handler to register 883 * @throws NullPointerException if the event type or handler is null 884 */ 885 public final <T extends Event> void addEventHandler( 886 final EventType<T> eventType, 887 final EventHandler<? super T> eventHandler) { 888 getInternalEventDispatcher().getEventHandlerManager() 889 .addEventHandler(eventType, eventHandler); 890 } 891 892 // PENDING_DOC_REVIEW 893 /** 894 * Unregisters a previously registered event handler from this node. One 895 * handler might have been registered for different event types, so the 896 * caller needs to specify the particular event type from which to 897 * unregister the handler. 898 * 899 * @param <T> the specific event class of the handler 900 * @param eventType the event type from which to unregister 901 * @param eventHandler the handler to unregister 902 * @throws NullPointerException if the event type or handler is null 903 */ 904 public final <T extends Event> void removeEventHandler( 905 final EventType<T> eventType, 906 final EventHandler<? super T> eventHandler) { 907 getInternalEventDispatcher().getEventHandlerManager() 908 .removeEventHandler(eventType, 909 eventHandler); 910 } 911 912 // PENDING_DOC_REVIEW 913 /** 914 * Registers an event filter to this node. The filter is called when the 915 * node receives an {@code Event} of the specified type during the capturing 916 * phase of event delivery. 917 * 918 * @param <T> the specific event class of the filter 919 * @param eventType the type of the events to receive by the filter 920 * @param eventFilter the filter to register 921 * @throws NullPointerException if the event type or filter is null 922 */ 923 public final <T extends Event> void addEventFilter( 924 final EventType<T> eventType, 925 final EventHandler<? super T> eventFilter) { 926 getInternalEventDispatcher().getEventHandlerManager() 927 .addEventFilter(eventType, eventFilter); 928 } 929 930 // PENDING_DOC_REVIEW 931 /** 932 * Unregisters a previously registered event filter from this node. One 933 * filter might have been registered for different event types, so the 934 * caller needs to specify the particular event type from which to 935 * unregister the filter. 936 * 937 * @param <T> the specific event class of the filter 938 * @param eventType the event type from which to unregister 939 * @param eventFilter the filter to unregister 940 * @throws NullPointerException if the event type or filter is null 941 */ 942 public final <T extends Event> void removeEventFilter( 943 final EventType<T> eventType, 944 final EventHandler<? super T> eventFilter) { 945 getInternalEventDispatcher().getEventHandlerManager() 946 .removeEventFilter(eventType, eventFilter); 947 } 948 949 /** 950 * Sets the handler to use for this event type. There can only be one such handler 951 * specified at a time. This handler is guaranteed to be called first. This is 952 * used for registering the user-defined onFoo event handlers. 953 * 954 * @param <T> the specific event class of the handler 955 * @param eventType the event type to associate with the given eventHandler 956 * @param eventHandler the handler to register, or null to unregister 957 * @throws NullPointerException if the event type is null 958 */ 959 protected final <T extends Event> void setEventHandler( 960 final EventType<T> eventType, 961 final EventHandler<? super T> eventHandler) { 962 getInternalEventDispatcher().getEventHandlerManager() 963 .setEventHandler(eventType, eventHandler); 964 } 965 966 WindowEventDispatcher getInternalEventDispatcher() { 967 initializeInternalEventDispatcher(); 968 return internalEventDispatcher; 969 } 970 971 private void initializeInternalEventDispatcher() { 972 if (internalEventDispatcher == null) { 973 internalEventDispatcher = createInternalEventDispatcher(); 974 eventDispatcher = new SimpleObjectProperty<EventDispatcher>( 975 this, 976 "eventDispatcher", 977 internalEventDispatcher); 978 } 979 } 980 981 WindowEventDispatcher createInternalEventDispatcher() { 982 return new WindowEventDispatcher(this); 983 } 984 985 /** 986 * Fires the specified event. 987 * <p> 988 * This method must be called on the FX user thread. 989 * 990 * @param event the event to fire 991 */ 992 public final void fireEvent(Event event) { 993 Event.fireEvent(this, event); 994 } 995 996 // PENDING_DOC_REVIEW 997 /** 998 * Construct an event dispatch chain for this window. 999 * 1000 * @param tail the initial chain to build from 1001 * @return the resulting event dispatch chain for this window 1002 */ 1003 @Override 1004 public EventDispatchChain buildEventDispatchChain( 1005 EventDispatchChain tail) { 1006 if (eventDispatcher != null) { 1007 final EventDispatcher eventDispatcherValue = eventDispatcher.get(); 1008 if (eventDispatcherValue != null) { 1009 tail = tail.prepend(eventDispatcherValue); 1010 } 1011 } 1012 1013 return tail; 1014 } 1015 1016 private int focusGrabCounter; 1017 1018 void increaseFocusGrabCounter() { 1019 if ((++focusGrabCounter == 1) && (impl_peer != null) && isFocused()) { 1020 impl_peer.grabFocus(); 1021 } 1022 } 1023 1024 void decreaseFocusGrabCounter() { 1025 if ((--focusGrabCounter == 0) && (impl_peer != null)) { 1026 impl_peer.ungrabFocus(); 1027 } 1028 } 1029 1030 private void focusChanged(final boolean newIsFocused) { 1031 if ((focusGrabCounter > 0) && (impl_peer != null) && newIsFocused) { 1032 impl_peer.grabFocus(); 1033 } 1034 } 1035 1036 final void applyBounds() { 1037 peerBoundsConfigurator.apply(); 1038 } 1039 1040 Window getWindowOwner() { 1041 return null; 1042 } 1043 1044 private Screen getWindowScreen() { 1045 Window window = this; 1046 do { 1047 if (!Double.isNaN(window.getX()) 1048 && !Double.isNaN(window.getY()) 1049 && !Double.isNaN(window.getWidth()) 1050 && !Double.isNaN(window.getHeight())) { 1051 return Utils.getScreenForRectangle( 1052 new Rectangle2D(window.getX(), 1053 window.getY(), 1054 window.getWidth(), 1055 window.getHeight())); 1056 } 1057 1058 window = window.getWindowOwner(); 1059 } while (window != null); 1060 1061 return Screen.getPrimary(); 1062 } 1063 1064 /** 1065 * Caches all requested bounds settings and applies them at once during 1066 * the next pulse. 1067 */ 1068 private final class TKBoundsConfigurator implements TKPulseListener { 1069 private double x; 1070 private double y; 1071 private float xGravity; 1072 private float yGravity; 1073 private double windowWidth; 1074 private double windowHeight; 1075 private double clientWidth; 1076 private double clientHeight; 1077 1078 private boolean dirty; 1079 1080 public TKBoundsConfigurator() { 1081 reset(); 1082 } 1083 1084 public void setX(final double x, final float xGravity) { 1085 this.x = x; 1086 this.xGravity = xGravity; 1087 setDirty(); 1088 } 1089 1090 public void setY(final double y, final float yGravity) { 1091 this.y = y; 1092 this.yGravity = yGravity; 1093 setDirty(); 1094 } 1095 1096 public void setWindowWidth(final double windowWidth) { 1097 this.windowWidth = windowWidth; 1098 setDirty(); 1099 } 1100 1101 public void setWindowHeight(final double windowHeight) { 1102 this.windowHeight = windowHeight; 1103 setDirty(); 1104 } 1105 1106 public void setClientWidth(final double clientWidth) { 1107 this.clientWidth = clientWidth; 1108 setDirty(); 1109 } 1110 1111 public void setClientHeight(final double clientHeight) { 1112 this.clientHeight = clientHeight; 1113 setDirty(); 1114 } 1115 1116 public void setLocation(final double x, 1117 final double y, 1118 final float xGravity, 1119 final float yGravity) { 1120 this.x = x; 1121 this.y = y; 1122 this.xGravity = xGravity; 1123 this.yGravity = yGravity; 1124 setDirty(); 1125 } 1126 1127 public void setSize(final double windowWidth, 1128 final double windowHeight, 1129 final double clientWidth, 1130 final double clientHeight) { 1131 this.windowWidth = windowWidth; 1132 this.windowHeight = windowHeight; 1133 this.clientWidth = clientWidth; 1134 this.clientHeight = clientHeight; 1135 setDirty(); 1136 } 1137 1138 public void apply() { 1139 if (dirty) { 1140 impl_peer.setBounds((float) (Double.isNaN(x) ? 0 : x), 1141 (float) (Double.isNaN(y) ? 0 : y), 1142 !Double.isNaN(x), 1143 !Double.isNaN(y), 1144 (float) windowWidth, 1145 (float) windowHeight, 1146 (float) clientWidth, 1147 (float) clientHeight, 1148 xGravity, yGravity); 1149 1150 reset(); 1151 } 1152 } 1153 1154 @Override 1155 public void pulse() { 1156 apply(); 1157 } 1158 1159 private void reset() { 1160 x = Double.NaN; 1161 y = Double.NaN; 1162 xGravity = 0; 1163 yGravity = 0; 1164 windowWidth = -1; 1165 windowHeight = -1; 1166 clientWidth = -1; 1167 clientHeight = -1; 1168 dirty = false; 1169 } 1170 1171 private void setDirty() { 1172 if (!dirty) { 1173 Toolkit.getToolkit().requestNextPulse(); 1174 dirty = true; 1175 } 1176 } 1177 } 1178}