1 /*
   2  * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.List;
  31 
  32 import javafx.application.Application;
  33 import javafx.beans.property.DoubleProperty;
  34 import javafx.beans.property.DoublePropertyBase;
  35 import javafx.beans.property.ObjectProperty;
  36 import javafx.beans.property.ObjectPropertyBase;
  37 import javafx.beans.property.StringProperty;
  38 import javafx.beans.value.WritableValue;
  39 import javafx.collections.FXCollections;
  40 import javafx.collections.ObservableList;
  41 import javafx.collections.ObservableSet;
  42 import javafx.css.CssParser;
  43 import javafx.scene.Node;
  44 import javafx.scene.Parent;
  45 import javafx.scene.Scene;
  46 import javafx.scene.layout.Pane;
  47 import javafx.stage.PopupWindow;
  48 import com.sun.javafx.application.PlatformImpl;
  49 import javafx.css.CssMetaData;
  50 import javafx.css.PseudoClass;
  51 import com.sun.javafx.css.StyleManager;
  52 import com.sun.javafx.scene.NodeHelper;
  53 import com.sun.javafx.scene.ParentHelper;
  54 import javafx.css.Styleable;
  55 import javafx.css.StyleableStringProperty;
  56 import javafx.css.converter.StringConverter;
  57 import com.sun.javafx.scene.control.Logging;
  58 import com.sun.javafx.scene.layout.PaneHelper;
  59 import com.sun.javafx.stage.PopupWindowHelper;
  60 import javafx.css.StyleableProperty;
  61 import javafx.stage.Window;
  62 import sun.util.logging.PlatformLogger;
  63 import sun.util.logging.PlatformLogger.Level;
  64 
  65 /**
  66  * An extension of PopupWindow that allows for CSS styling.
  67  * @since JavaFX 2.0
  68  */
  69 public class PopupControl extends PopupWindow implements Skinnable, Styleable {
  70 
  71     /**
  72      * Sentinel value which can be passed to a control's setMinWidth(), setMinHeight(),
  73      * setMaxWidth() or setMaxHeight() methods to indicate that the preferred dimension
  74      * should be used for that max and/or min constraint.
  75      */
  76     public static final double USE_PREF_SIZE = Double.NEGATIVE_INFINITY;
  77 
  78      /**
  79       * Sentinel value which can be passed to a control's setMinWidth(), setMinHeight(),
  80       * setPrefWidth(), setPrefHeight(), setMaxWidth(), setMaxHeight() methods
  81       * to reset the control's size constraint back to it's intrinsic size returned
  82       * by computeMinWidth(), computeMinHeight(), computePrefWidth(), computePrefHeight(),
  83       * computeMaxWidth(), or computeMaxHeight().
  84       */
  85     public static final double USE_COMPUTED_SIZE = -1;
  86 
  87     static {
  88         // Ensures that the default application user agent stylesheet is loaded
  89         if (Application.getUserAgentStylesheet() == null) {
  90             PlatformImpl.setDefaultPlatformUserAgentStylesheet();
  91         }
  92     }
  93 
  94     /**
  95      * We need a special root node, except we can't replace the special
  96      * root node already in the PopupControl. So we'll set our own
  97      * special almost-root node that is a child of the root.
  98      *
  99      * This special root node is responsible for mapping the id, styleClass,
 100      * and style defined on the PopupControl such that CSS will read the
 101      * values from the PopupControl, and then apply CSS state to that
 102      * special node. The node will then be able to pass cssSet calls
 103      * along, such that any subclass of PopupControl will be able to
 104      * use the Styleable properties  and we'll be able to style it from
 105      * CSS, in such a way that it participates and applies to the skin,
 106      * exactly the way that normal Skin's work for normal Controls.
 107      * @since JavaFX 2.1
 108      */
 109     protected CSSBridge bridge;
 110 
 111     /**
 112      * Create a new empty PopupControl.
 113      */
 114     public PopupControl() {
 115         super();
 116         this.bridge = new CSSBridge();
 117         setAnchorLocation(AnchorLocation.CONTENT_TOP_LEFT);
 118         PopupWindowHelper.getContent(this).add(bridge);
 119     }
 120 
 121     // TODO the fact that PopupWindow uses a group for auto-moving things
 122     // around means that the scene resize semantics don't work if the
 123     // child is a resizable. I will need to replicate those semantics
 124     // here sometime, such that if the Skin provides a resizable, it is
 125     // given to match the popup window's width & height.
 126 
 127     /**
 128      * The id of this {@code PopupControl}. This simple string identifier is useful for
 129      * finding a specific Node within the scene graph. While the id of a Node
 130      * should be unique within the scene graph, this uniqueness is not enforced.
 131      * This is analogous to the "id" attribute on an HTML element
 132      * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
 133      *
 134      * @return the id property
 135      * @defaultValue null
 136      */
 137     public final StringProperty idProperty() { return bridge.idProperty(); }
 138 
 139     /**
 140      * Sets the id of this {@code PopupControl}. This simple string identifier is useful for
 141      * finding a specific Node within the scene graph. While the id of a Node
 142      * should be unique within the scene graph, this uniqueness is not enforced.
 143      * This is analogous to the "id" attribute on an HTML element
 144      * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
 145      *
 146      * @param value  the id assigned to this {@code PopupControl} using the {@code setId}
 147      *         method or {@code null}, if no id has been assigned.
 148      * @defaultValue null
 149      */
 150     public final void setId(String value) { idProperty().set(value); }
 151 
 152     /**
 153      * The id of this {@code PopupControl}. This simple string identifier is useful for
 154      * finding a specific Node within the scene graph. While the id of a Node
 155      * should be unique within the scene graph, this uniqueness is not enforced.
 156      * This is analogous to the "id" attribute on an HTML element
 157      * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
 158      *
 159      * @return the id assigned to this {@code PopupControl} using the {@code setId}
 160      *         method or {@code null}, if no id has been assigned.
 161      * @defaultValue null
 162      */
 163     @Override public final String getId() { return idProperty().get(); }
 164 
 165     /**
 166      * Returns the list of String identifiers that make up the styleClass
 167      * for this PopupControl.
 168      */
 169     @Override public final ObservableList<String> getStyleClass() { return bridge.getStyleClass(); }
 170 
 171     /**
 172      * A string representation of the CSS style associated with this
 173      * specific {@code PopupControl}. This is analogous to the "style" attribute of an
 174      * HTML element. Note that, like the HTML style attribute, this
 175      * variable contains style properties and values and not the
 176      * selector portion of a style rule.
 177      * @param value The inline CSS style to use for this {@code PopupControl}.
 178      *         {@code null} is implicitly converted to an empty String.
 179      * @defaultValue empty string
 180      */
 181     public final void setStyle(String value) { styleProperty().set(value); }
 182 
 183     // TODO: javadoc copied from property for the sole purpose of providing a return tag
 184     /**
 185      * A string representation of the CSS style associated with this
 186      * specific {@code PopupControl}. This is analogous to the "style" attribute of an
 187      * HTML element. Note that, like the HTML style attribute, this
 188      * variable contains style properties and values and not the
 189      * selector portion of a style rule.
 190      * @defaultValue empty string
 191      * @return The inline CSS style associated with this {@code PopupControl}.
 192      *         If this {@code PopupControl} does not have an inline style,
 193      *         an empty String is returned.
 194      */
 195     @Override public final String getStyle() { return styleProperty().get(); }
 196     public final StringProperty styleProperty() { return bridge.styleProperty(); }
 197 
 198     /**
 199      * Skin is responsible for rendering this {@code PopupControl}. From the
 200      * perspective of the {@code PopupControl}, the {@code Skin} is a black box.
 201      * It listens and responds to changes in state in a {@code PopupControl}.
 202      * <p>
 203      * There is a one-to-one relationship between a {@code PopupControl} and its
 204      * {@code Skin}. Every {@code Skin} maintains a back reference to the
 205      * {@code PopupControl}.
 206      * <p>
 207      * A skin may be null.
 208      */
 209     @Override public final ObjectProperty<Skin<?>> skinProperty() {
 210         return skin;
 211     }
 212 
 213     @Override public final void setSkin(Skin<?> value) {
 214         skinProperty().setValue(value);
 215     }
 216 
 217     @Override public final Skin<?> getSkin() {
 218         return skinProperty().getValue();
 219     }
 220 
 221     private final ObjectProperty<Skin<?>> skin = new ObjectPropertyBase<Skin<?>>() {
 222         // We store a reference to the oldValue so that we can handle
 223         // changes in the skin properly in the case of binding. This is
 224         // only needed because invalidated() does not currently take
 225         // a reference to the old value.
 226         private Skin<?> oldValue;
 227 
 228         @Override
 229         public void set(Skin<?> v) {
 230 
 231             if (v == null
 232                     ? oldValue == null
 233                     : oldValue != null && v.getClass().equals(oldValue.getClass()))
 234                 return;
 235 
 236             super.set(v);
 237 
 238         }
 239 
 240         @Override protected void invalidated() {
 241             Skin<?> skin = get();
 242 
 243             // Collect the name of the currently installed skin class. We do this
 244             // so that subsequent updates from CSS to the same skin class will not
 245             // result in reinstalling the skin
 246             currentSkinClassName = skin == null ? null : skin.getClass().getName();
 247 
 248             // if someone calls setSkin, we need to make it look like they
 249             // called set on skinClassName in order to keep CSS from overwriting
 250             // the skin.
 251             skinClassNameProperty().set(currentSkinClassName);
 252 
 253             // Let CSS know that this property has been manually changed
 254             // Dispose of the old skin
 255             if (oldValue != null) {
 256                 oldValue.dispose();
 257             }
 258 
 259             // Get the new value, and save it off as the new oldValue
 260             oldValue = getValue();
 261 
 262             prefWidthCache = -1;
 263             prefHeightCache = -1;
 264             minWidthCache = -1;
 265             minHeightCache = -1;
 266             maxWidthCache = -1;
 267             maxHeightCache = -1;
 268             skinSizeComputed = false;
 269 
 270             final Node n = getSkinNode();
 271             if (n != null) {
 272                 bridge.getChildren().setAll(n);
 273             } else {
 274                 bridge.getChildren().clear();
 275             }
 276 
 277             // calling NodeHelper.reapplyCSS() as the styleable properties may now
 278             // be different, as we will now be able to return styleable properties
 279             // belonging to the skin. If NodeHelper.reapplyCSS() is not called, the
 280             // getCssMetaData() method is never called, so the
 281             // skin properties are never exposed.
 282             NodeHelper.reapplyCSS(bridge);
 283 
 284             // DEBUG: Log that we've changed the skin
 285             final PlatformLogger logger = Logging.getControlsLogger();
 286             if (logger.isLoggable(Level.FINEST)) {
 287                 logger.finest("Stored skin[" + getValue() + "] on " + this);
 288             }
 289         }
 290 
 291         @Override
 292         public Object getBean() {
 293             return PopupControl.this;
 294         }
 295 
 296         @Override
 297         public String getName() {
 298             return "skin";
 299         }
 300     };
 301     /**
 302      * Keeps a reference to the name of the class currently acting as the skin.
 303      */
 304     private String currentSkinClassName = null;
 305     /**
 306      * A property that acts as a proxy between the skin property and css.
 307      */
 308     private StringProperty skinClassName = null;
 309     private StringProperty skinClassNameProperty() {
 310         if (skinClassName == null) {
 311             skinClassName = new StyleableStringProperty() {
 312 
 313                 @Override
 314                 public void set(String v) {
 315                     // do not allow the skin to be set to null through CSS
 316                     if (v == null || v.isEmpty() || v.equals(get())) return;
 317                     super.set(v);
 318                 }
 319 
 320                 @Override
 321                 public void invalidated() {
 322 
 323                     //
 324                     // if the current skin is not null, then
 325                     // see if then check to see if the current skin's class name
 326                     // is the same as skinClassName. If it is, then there is
 327                     // no need to load the skin class. Note that the only time
 328                     // this would be the case is if someone called setSkin since
 329                     // the skin would be set ahead of the skinClassName
 330                     // (skinClassName is set from the skinProperty's invalidated
 331                     // method, so the skin would be set, then the skinClassName).
 332                     // If the skinClassName is set first (via CSS), then this
 333                     // invalidated method won't get called unless the value
 334                     // has changed (thus, we won't reload the same skin).
 335                     //
 336                     if (get() != null) {
 337                         if (!get().equals(currentSkinClassName)) {
 338                             Control.loadSkinClass(PopupControl.this, get());
 339                         }
 340                         // CSS should not set skin to null
 341                         //                    } else {
 342                         //                        setSkin(null);
 343                     }
 344                 }
 345 
 346                 @Override
 347                 public Object getBean() {
 348                     return PopupControl.this;
 349                 }
 350 
 351                 @Override
 352                 public String getName() {
 353                     return "skinClassName";
 354                 }
 355 
 356                 @Override
 357                 public CssMetaData<CSSBridge,String> getCssMetaData() {
 358                     return SKIN;
 359                 }
 360 
 361             };
 362         }
 363         return skinClassName;
 364     }
 365 
 366     /**
 367      * Gets the Skin's node, or returns null if there is no Skin.
 368      * Convenience method for getting the node of the skin. This is null-safe,
 369      * meaning if skin is null then it will return null instead of throwing
 370      * a NullPointerException.
 371      *
 372      * @return The Skin's node, or null.
 373      */
 374     private Node getSkinNode() {
 375         return getSkin() == null ? null : getSkin().getNode();
 376     }
 377 
 378     /**
 379      * Property for overriding the control's computed minimum width.
 380      * This should only be set if the control's internally computed minimum width
 381      * doesn't meet the application's layout needs.
 382      * <p>
 383      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 384      * <code>getMinWidth(forHeight)</code> will return the control's internally
 385      * computed minimum width.
 386      * <p>
 387      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 388      * <code>getMinWidth(forHeight)</code> to return the control's preferred width,
 389      * enabling applications to easily restrict the resizability of the control.
 390      */
 391     private DoubleProperty minWidth;
 392 
 393     /**
 394      * Property for overriding the control's computed minimum width.
 395      * This should only be set if the control's internally computed minimum width
 396      * doesn't meet the application's layout needs.
 397      * <p>
 398      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 399      * <code>getMinWidth(forHeight)</code> will return the control's internally
 400      * computed minimum width.
 401      * <p>
 402      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 403      * <code>getMinWidth(forHeight)</code> to return the control's preferred width,
 404      * enabling applications to easily restrict the resizability of the control.
 405      * @param value the minimum width
 406      */
 407     public final void setMinWidth(double value) { minWidthProperty().set(value); }
 408 
 409     /**
 410      * Property for overriding the control's computed minimum width.
 411      * This should only be set if the control's internally computed minimum width
 412      * doesn't meet the application's layout needs.
 413      * <p>
 414      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 415      * <code>getMinWidth(forHeight)</code> will return the control's internally
 416      * computed minimum width.
 417      * <p>
 418      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 419      * <code>getMinWidth(forHeight)</code> to return the control's preferred width,
 420      * enabling applications to easily restrict the resizability of the control.
 421      * @return the minimum width
 422      */
 423     public final double getMinWidth() { return minWidth == null ? USE_COMPUTED_SIZE : minWidth.get(); }
 424     public final DoubleProperty minWidthProperty() {
 425         if (minWidth == null) {
 426             minWidth = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 427                 @Override public void invalidated() {
 428                     if (isShowing()) bridge.requestLayout();
 429                 }
 430 
 431                 @Override
 432                 public Object getBean() {
 433                     return PopupControl.this;
 434                 }
 435 
 436                 @Override
 437                 public String getName() {
 438                     return "minWidth";
 439                 }
 440             };
 441         }
 442         return minWidth;
 443     }
 444 
 445 
 446     /**
 447      * Property for overriding the control's computed minimum height.
 448      * This should only be set if the control's internally computed minimum height
 449      * doesn't meet the application's layout needs.
 450      * <p>
 451      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 452      * <code>getMinHeight(forWidth)</code> will return the control's internally
 453      * computed minimum height.
 454      * <p>
 455      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 456      * <code>getMinHeight(forWidth)</code> to return the control's preferred height,
 457      * enabling applications to easily restrict the resizability of the control.
 458      *
 459      */
 460     private DoubleProperty minHeight;
 461 
 462     /**
 463      * Property for overriding the control's computed minimum height.
 464      * This should only be set if the control's internally computed minimum height
 465      * doesn't meet the application's layout needs.
 466      * <p>
 467      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 468      * <code>getMinHeight(forWidth)</code> will return the control's internally
 469      * computed minimum height.
 470      * <p>
 471      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 472      * <code>getMinHeight(forWidth)</code> to return the control's preferred height,
 473      * enabling applications to easily restrict the resizability of the control.
 474      *
 475      * @param value the minimum height
 476      */
 477     public final void setMinHeight(double value) { minHeightProperty().set(value); }
 478 
 479     /**
 480      * Property for overriding the control's computed minimum height.
 481      * This should only be set if the control's internally computed minimum height
 482      * doesn't meet the application's layout needs.
 483      * <p>
 484      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 485      * <code>getMinHeight(forWidth)</code> will return the control's internally
 486      * computed minimum height.
 487      * <p>
 488      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 489      * <code>getMinHeight(forWidth)</code> to return the control's preferred height,
 490      * enabling applications to easily restrict the resizability of the control.
 491      *
 492      * @return the minimum height
 493      */
 494     public final double getMinHeight() { return minHeight == null ? USE_COMPUTED_SIZE : minHeight.get(); }
 495     public final DoubleProperty minHeightProperty() {
 496         if (minHeight == null) {
 497             minHeight = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 498                 @Override public void invalidated() {
 499                     if (isShowing()) bridge.requestLayout();
 500                 }
 501 
 502                 @Override
 503                 public Object getBean() {
 504                     return PopupControl.this;
 505                 }
 506 
 507                 @Override
 508                 public String getName() {
 509                     return "minHeight";
 510                 }
 511             };
 512         }
 513         return minHeight;
 514     }
 515 
 516     /**
 517      * Convenience method for overriding the control's computed minimum width and height.
 518      * This should only be called if the control's internally computed minimum size
 519      * doesn't meet the application's layout needs.
 520      *
 521      * @see #setMinWidth
 522      * @see #setMinHeight
 523      * @param minWidth  the override value for minimum width
 524      * @param minHeight the override value for minimum height
 525      */
 526     public void setMinSize(double minWidth, double minHeight) {
 527         setMinWidth(minWidth);
 528         setMinHeight(minHeight);
 529     }
 530 
 531     /**
 532      * Property for overriding the control's computed preferred width.
 533      * This should only be set if the control's internally computed preferred width
 534      * doesn't meet the application's layout needs.
 535      * <p>
 536      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 537      * <code>getPrefWidth(forHeight)</code> will return the control's internally
 538      * computed preferred width.
 539      */
 540     private DoubleProperty prefWidth;
 541 
 542     /**
 543      * Property for overriding the control's computed preferred width.
 544      * This should only be set if the control's internally computed preferred width
 545      * doesn't meet the application's layout needs.
 546      * <p>
 547      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 548      * <code>getPrefWidth(forHeight)</code> will return the control's internally
 549      * computed preferred width.
 550      * @param value the preferred width
 551      */
 552     public final void setPrefWidth(double value) { prefWidthProperty().set(value); }
 553 
 554     /**
 555      * Property for overriding the control's computed preferred width.
 556      * This should only be set if the control's internally computed preferred width
 557      * doesn't meet the application's layout needs.
 558      * <p>
 559      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 560      * <code>getPrefWidth(forHeight)</code> will return the control's internally
 561      * computed preferred width.
 562      * @return the preferred width
 563      */
 564     public final double getPrefWidth() { return prefWidth == null ? USE_COMPUTED_SIZE : prefWidth.get(); }
 565     public final DoubleProperty prefWidthProperty() {
 566         if (prefWidth == null) {
 567             prefWidth = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 568                 @Override public void invalidated() {
 569                     if (isShowing()) bridge.requestLayout();
 570                 }
 571 
 572                 @Override
 573                 public Object getBean() {
 574                     return PopupControl.this;
 575                 }
 576 
 577                 @Override
 578                 public String getName() {
 579                     return "prefWidth";
 580                 }
 581             };
 582         }
 583         return prefWidth;
 584     }
 585 
 586     /**
 587      * Property for overriding the control's computed preferred height.
 588      * This should only be set if the control's internally computed preferred height
 589      * doesn't meet the application's layout needs.
 590      * <p>
 591      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 592      * <code>getPrefHeight(forWidth)</code> will return the control's internally
 593      * computed preferred width.
 594      *
 595      */
 596     private DoubleProperty prefHeight;
 597 
 598     /**
 599      * Property for overriding the control's computed preferred height.
 600      * This should only be set if the control's internally computed preferred height
 601      * doesn't meet the application's layout needs.
 602      * <p>
 603      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 604      * <code>getPrefHeight(forWidth)</code> will return the control's internally
 605      * computed preferred width.
 606      *
 607      * @param value the preferred height
 608      */
 609     public final void setPrefHeight(double value) { prefHeightProperty().set(value); }
 610 
 611     /**
 612      * Property for overriding the control's computed preferred height.
 613      * This should only be set if the control's internally computed preferred height
 614      * doesn't meet the application's layout needs.
 615      * <p>
 616      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 617      * <code>getPrefHeight(forWidth)</code> will return the control's internally
 618      * computed preferred width.
 619      *
 620      * @return the preferred height
 621      */
 622     public final double getPrefHeight() { return prefHeight == null ? USE_COMPUTED_SIZE : prefHeight.get(); }
 623     public final DoubleProperty prefHeightProperty() {
 624         if (prefHeight == null) {
 625             prefHeight = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 626                 @Override public void invalidated() {
 627                     if (isShowing()) bridge.requestLayout();
 628                 }
 629 
 630                 @Override
 631                 public Object getBean() {
 632                     return PopupControl.this;
 633                 }
 634 
 635                 @Override
 636                 public String getName() {
 637                     return "prefHeight";
 638                 }
 639             };
 640         }
 641         return prefHeight;
 642     }
 643 
 644     /**
 645      * Convenience method for overriding the control's computed preferred width and height.
 646      * This should only be called if the control's internally computed preferred size
 647      * doesn't meet the application's layout needs.
 648      *
 649      * @see #setPrefWidth
 650      * @see #setPrefHeight
 651      * @param prefWidth the override value for preferred width
 652      * @param prefHeight the override value for preferred height
 653      */
 654     public void setPrefSize(double prefWidth, double prefHeight) {
 655         setPrefWidth(prefWidth);
 656         setPrefHeight(prefHeight);
 657     }
 658 
 659     /**
 660      * Property for overriding the control's computed maximum width.
 661      * This should only be set if the control's internally computed maximum width
 662      * doesn't meet the application's layout needs.
 663      * <p>
 664      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 665      * <code>getMaxWidth(forHeight)</code> will return the control's internally
 666      * computed maximum width.
 667      * <p>
 668      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 669      * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
 670      * enabling applications to easily restrict the resizability of the control.
 671      */
 672     private DoubleProperty maxWidth;
 673 
 674     /**
 675      * Property for overriding the control's computed maximum width.
 676      * This should only be set if the control's internally computed maximum width
 677      * doesn't meet the application's layout needs.
 678      * <p>
 679      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 680      * <code>getMaxWidth(forHeight)</code> will return the control's internally
 681      * computed maximum width.
 682      * <p>
 683      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 684      * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
 685      * enabling applications to easily restrict the resizability of the control.
 686      * @param value the maximum width
 687      */
 688     public final void setMaxWidth(double value) { maxWidthProperty().set(value); }
 689 
 690     /**
 691      * Property for overriding the control's computed maximum width.
 692      * This should only be set if the control's internally computed maximum width
 693      * doesn't meet the application's layout needs.
 694      * <p>
 695      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 696      * <code>getMaxWidth(forHeight)</code> will return the control's internally
 697      * computed maximum width.
 698      * <p>
 699      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 700      * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
 701      * enabling applications to easily restrict the resizability of the control.
 702      * @return the maximum width
 703      */
 704     public final double getMaxWidth() { return maxWidth == null ? USE_COMPUTED_SIZE : maxWidth.get(); }
 705     public final DoubleProperty maxWidthProperty() {
 706         if (maxWidth == null) {
 707             maxWidth = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 708                 @Override public void invalidated() {
 709                     if (isShowing()) bridge.requestLayout();
 710                 }
 711 
 712                 @Override
 713                 public Object getBean() {
 714                     return PopupControl.this;
 715                 }
 716 
 717                 @Override
 718                 public String getName() {
 719                     return "maxWidth";
 720                 }
 721             };
 722         }
 723         return maxWidth;
 724     }
 725 
 726     /**
 727      * Property for overriding the control's computed maximum height.
 728      * This should only be set if the control's internally computed maximum height
 729      * doesn't meet the application's layout needs.
 730      * <p>
 731      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 732      * <code>getMaxHeight(forWidth)</code> will return the control's internally
 733      * computed maximum height.
 734      * <p>
 735      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 736      * <code>getMaxHeight(forWidth)</code> to return the control's preferred height,
 737      * enabling applications to easily restrict the resizability of the control.
 738      *
 739      */
 740     private DoubleProperty maxHeight;
 741 
 742     /**
 743      * Property for overriding the control's computed maximum height.
 744      * This should only be set if the control's internally computed maximum height
 745      * doesn't meet the application's layout needs.
 746      * <p>
 747      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 748      * <code>getMaxHeight(forWidth)</code> will return the control's internally
 749      * computed maximum height.
 750      * <p>
 751      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 752      * <code>getMaxHeight(forWidth)</code> to return the control's preferred height,
 753      * enabling applications to easily restrict the resizability of the control.
 754      *
 755      * @param value the maximum height
 756      */
 757     public final void setMaxHeight(double value) { maxHeightProperty().set(value); }
 758 
 759     /**
 760      * Property for overriding the control's computed maximum height.
 761      * This should only be set if the control's internally computed maximum height
 762      * doesn't meet the application's layout needs.
 763      * <p>
 764      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 765      * <code>getMaxHeight(forWidth)</code> will return the control's internally
 766      * computed maximum height.
 767      * <p>
 768      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 769      * <code>getMaxHeight(forWidth)</code> to return the control's preferred height,
 770      * enabling applications to easily restrict the resizability of the control.
 771      *
 772      * @return the maximum height
 773      */
 774     public final double getMaxHeight() { return maxHeight == null ? USE_COMPUTED_SIZE : maxHeight.get(); }
 775     public final DoubleProperty maxHeightProperty() {
 776         if (maxHeight == null) {
 777             maxHeight = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 778                 @Override public void invalidated() {
 779                     if (isShowing()) bridge.requestLayout();
 780                 }
 781 
 782                 @Override
 783                 public Object getBean() {
 784                     return PopupControl.this;
 785                 }
 786 
 787                 @Override
 788                 public String getName() {
 789                     return "maxHeight";
 790                 }
 791             };
 792         }
 793         return maxHeight;
 794     }
 795 
 796     /**
 797      * Convenience method for overriding the control's computed maximum width and height.
 798      * This should only be called if the control's internally computed maximum size
 799      * doesn't meet the application's layout needs.
 800      *
 801      * @see #setMaxWidth
 802      * @see #setMaxHeight
 803      * @param maxWidth  the override value for maximum width
 804      * @param maxHeight the override value for maximum height
 805      */
 806     public void setMaxSize(double maxWidth, double maxHeight) {
 807         setMaxWidth(maxWidth);
 808         setMaxHeight(maxHeight);
 809     }
 810 
 811     /**
 812      * Cached prefWidth, prefHeight, minWidth, minHeight. These
 813      * results are repeatedly sought during the layout pass,
 814      * and caching the results leads to a significant decrease
 815      * in overhead.
 816      */
 817     private double prefWidthCache = -1;
 818     private double prefHeightCache = -1;
 819     private double minWidthCache = -1;
 820     private double minHeightCache = -1;
 821     private double maxWidthCache = -1;
 822     private double maxHeightCache = -1;
 823     private boolean skinSizeComputed = false;
 824 
 825     /**
 826      * Called during layout to determine the minimum width for this node.
 827      * Returns the value from <code>minWidth(forHeight)</code> unless
 828      * the application overrode the minimum width by setting the minWidth property.
 829      *
 830      * @param height the height
 831      * @see #setMinWidth
 832      * @return the minimum width that this node should be resized to during layout
 833      */
 834     public final double minWidth(double height) {
 835         double override = getMinWidth();
 836         if (override == USE_COMPUTED_SIZE) {
 837             if (minWidthCache == -1) minWidthCache = recalculateMinWidth(height);
 838             return minWidthCache;
 839         } else if (override == USE_PREF_SIZE) {
 840             return prefWidth(height);
 841         }
 842         return override;
 843     }
 844 
 845     /**
 846      * Called during layout to determine the minimum height for this node.
 847      * Returns the value from <code>minHeight(forWidth)</code> unless
 848      * the application overrode the minimum height by setting the minHeight property.
 849      *
 850      * @param width The width
 851      * @see #setMinHeight
 852      * @return the minimum height that this node should be resized to during layout
 853      */
 854     public final double minHeight(double width) {
 855         double override = getMinHeight();
 856         if (override == USE_COMPUTED_SIZE) {
 857             if (minHeightCache == -1) minHeightCache = recalculateMinHeight(width);
 858             return minHeightCache;
 859         } else if (override == USE_PREF_SIZE) {
 860             return prefHeight(width);
 861         }
 862         return override;
 863     }
 864 
 865 
 866     /**
 867      * Called during layout to determine the preferred width for this node.
 868      * Returns the value from <code>prefWidth(forHeight)</code> unless
 869      * the application overrode the preferred width by setting the prefWidth property.
 870      *
 871      * @param height the height
 872      * @see #setPrefWidth
 873      * @return the preferred width that this node should be resized to during layout
 874      */
 875     public final double prefWidth(double height) {
 876         double override = getPrefWidth();
 877         if (override == USE_COMPUTED_SIZE) {
 878             if (prefWidthCache == -1) prefWidthCache = recalculatePrefWidth(height);
 879             return prefWidthCache;
 880         } else if (override == USE_PREF_SIZE) {
 881             return prefWidth(height);
 882         }
 883         return override;
 884     }
 885 
 886     /**
 887      * Called during layout to determine the preferred height for this node.
 888      * Returns the value from <code>prefHeight(forWidth)</code> unless
 889      * the application overrode the preferred height by setting the prefHeight property.
 890      *
 891      * @param width the width
 892      * @see #setPrefHeight
 893      * @return the preferred height that this node should be resized to during layout
 894      */
 895     public final double prefHeight(double width) {
 896         double override = getPrefHeight();
 897         if (override == USE_COMPUTED_SIZE) {
 898             if (prefHeightCache == -1) prefHeightCache = recalculatePrefHeight(width);
 899             return prefHeightCache;
 900         } else if (override == USE_PREF_SIZE) {
 901             return prefHeight(width);
 902         }
 903         return override;
 904     }
 905 
 906     /**
 907      * Called during layout to determine the maximum width for this node.
 908      * Returns the value from <code>maxWidth(forHeight)</code> unless
 909      * the application overrode the maximum width by setting the maxWidth property.
 910      *
 911      * @param height the height
 912      * @see #setMaxWidth
 913      * @return the maximum width that this node should be resized to during layout
 914      */
 915     public final double maxWidth(double height) {
 916         double override = getMaxWidth();
 917         if (override == USE_COMPUTED_SIZE) {
 918             if (maxWidthCache == -1) maxWidthCache = recalculateMaxWidth(height);
 919             return maxWidthCache;
 920         } else if (override == USE_PREF_SIZE) {
 921             return prefWidth(height);
 922         }
 923         return override;
 924     }
 925 
 926     /**
 927      * Called during layout to determine the maximum height for this node.
 928      * Returns the value from <code>maxHeight(forWidth)</code> unless
 929      * the application overrode the maximum height by setting the maxHeight property.
 930      *
 931      * @param width the width
 932      * @see #setMaxHeight
 933      * @return the maximum height that this node should be resized to during layout
 934      */
 935     public final double maxHeight(double width) {
 936         double override = getMaxHeight();
 937         if (override == USE_COMPUTED_SIZE) {
 938             if (maxHeightCache == -1) maxHeightCache = recalculateMaxHeight(width);
 939             return maxHeightCache;
 940         } else if (override == USE_PREF_SIZE) {
 941             return prefHeight(width);
 942         }
 943         return override;
 944     }
 945 
 946     // Implementation of the Resizable interface.
 947     // Because only the skin can know the min, pref, and max sizes, these
 948     // functions are implemented to delegate to skin. If there is no skin then
 949     // we simply return 0 for all the values since a Control without a Skin
 950     // doesn't render
 951     private double recalculateMinWidth(double height) {
 952         recomputeSkinSize();
 953         return getSkinNode() == null ? 0 : getSkinNode().minWidth(height);
 954     }
 955     private double recalculateMinHeight(double width) {
 956         recomputeSkinSize();
 957         return getSkinNode() == null ? 0 : getSkinNode().minHeight(width);
 958     }
 959     private double recalculateMaxWidth(double height) {
 960         recomputeSkinSize();
 961         return getSkinNode() == null ? 0 : getSkinNode().maxWidth(height);
 962     }
 963     private double recalculateMaxHeight(double width) {
 964         recomputeSkinSize();
 965         return getSkinNode() == null ? 0 : getSkinNode().maxHeight(width);
 966     }
 967     private double recalculatePrefWidth(double height) {
 968         recomputeSkinSize();
 969         return getSkinNode() == null? 0 : getSkinNode().prefWidth(height);
 970     }
 971     private double recalculatePrefHeight(double width) {
 972         recomputeSkinSize();
 973         return getSkinNode() == null? 0 : getSkinNode().prefHeight(width);
 974     }
 975 
 976     private void recomputeSkinSize() {
 977         if (!skinSizeComputed) {
 978             // RT-14094, RT-16754: We need the skins of the popup
 979             // and it children before the stage is visible so we
 980             // can calculate the popup position based on content
 981             // size.
 982             bridge.applyCss();
 983             skinSizeComputed = true;
 984         }
 985     }
 986 //    public double getBaselineOffset() { return getSkinNode() == null? 0 : getSkinNode().getBaselineOffset(); }
 987 
 988     /**
 989      * Create a new instance of the default skin for this control. This is called to create a skin for the control if
 990      * no skin is provided via CSS {@code -fx-skin} or set explicitly in a sub-class with {@code  setSkin(...)}.
 991      *
 992      * @return  new instance of default skin for this control. If null then the control will have no skin unless one
 993      *          is provided by css.
 994      * @since JavaFX 8.0
 995      */
 996     protected Skin<?> createDefaultSkin() {
 997         return null;
 998     }
 999 
1000     /***************************************************************************
1001      *                                                                         *
1002      *                         StyleSheet Handling                             *
1003      *                                                                         *
1004      **************************************************************************/
1005 
1006     private static final CssMetaData<CSSBridge,String> SKIN =
1007             new CssMetaData<CSSBridge,String>("-fx-skin",
1008                     StringConverter.getInstance()) {
1009 
1010                 @Override
1011                 public boolean isSettable(CSSBridge cssBridge) {
1012                     return !cssBridge.popupControl.skinProperty().isBound();
1013                 }
1014 
1015                 @Override
1016                 public StyleableProperty<String> getStyleableProperty(CSSBridge cssBridge) {
1017                     return (StyleableProperty<String>)(WritableValue<String>)cssBridge.popupControl.skinClassNameProperty();
1018                 }
1019             };
1020 
1021     private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
1022     static {
1023         final List<CssMetaData<? extends Styleable, ?>> styleables =
1024                 new ArrayList<CssMetaData<? extends Styleable, ?>>();
1025         Collections.addAll(styleables,
1026                 SKIN
1027         );
1028         STYLEABLES = Collections.unmodifiableList(styleables);
1029     }
1030 
1031     /**
1032      * @return The CssMetaData associated with this class, which may include the
1033      * CssMetaData of its superclasses.
1034      * @since JavaFX 8.0
1035      */
1036     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
1037         return STYLEABLES;
1038     }
1039 
1040     /**
1041      * {@inheritDoc}
1042      * @since JavaFX 8.0
1043      */
1044     @Override
1045     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
1046         return getClassCssMetaData();
1047     }
1048 
1049     /**
1050      * @param pseudoClass the pseudo class
1051      * @param active the active state
1052      * @see Node#pseudoClassStateChanged(javafx.css.PseudoClass, boolean)
1053      * @since JavaFX 8.0
1054      */
1055     public final void pseudoClassStateChanged(PseudoClass pseudoClass, boolean active) {
1056         bridge.pseudoClassStateChanged(pseudoClass, active);
1057     }
1058 
1059     /**
1060      * {@inheritDoc}
1061      * @return "PopupControl"
1062      * @since JavaFX 8.0
1063      */
1064     @Override
1065     public String getTypeSelector() {
1066         return "PopupControl";
1067     }
1068 
1069     /**
1070      * {@inheritDoc}
1071      *
1072      * {@literal A PopupControl's styles are based on the popup "owner" which is the}
1073      * {@link javafx.stage.PopupWindow#getOwnerNode() ownerNode} or,
1074      * if the ownerNode is not set, the root of the
1075      * {@link javafx.stage.PopupWindow#getOwnerWindow() ownerWindow's}
1076      * scene. If the popup has not been shown, both ownerNode and ownerWindow will be null and {@code null} will be returned.
1077      *
1078      * {@literal Note that the PopupWindow's scene root is not returned because
1079      * there is no way to guarantee that the PopupWindow's scene root would
1080      * properly return the ownerNode or ownerWindow.}
1081      *
1082      * @return {@link javafx.stage.PopupWindow#getOwnerNode()}, {@link javafx.stage.PopupWindow#getOwnerWindow()},
1083      * or null.
1084      * @since JavaFX 8.0
1085      */
1086     @Override
1087     public Styleable getStyleableParent() {
1088 
1089         final Node ownerNode = getOwnerNode();
1090         if (ownerNode != null) {
1091             return ownerNode;
1092 
1093         } else {
1094 
1095             final Window ownerWindow = getOwnerWindow();
1096             if (ownerWindow != null) {
1097 
1098                 final Scene ownerScene = ownerWindow.getScene();
1099                 if (ownerScene != null) {
1100                     return ownerScene.getRoot();
1101                 }
1102             }
1103         }
1104 
1105         return bridge.getParent();
1106 
1107     }
1108 
1109     /**
1110      * {@inheritDoc}
1111      * @since JavaFX 8.0
1112      */
1113     @Override
1114     public final ObservableSet<PseudoClass> getPseudoClassStates() {
1115         return FXCollections.emptyObservableSet();
1116     }
1117 
1118     /** {@inheritDoc} */
1119     @Override public Node getStyleableNode() {
1120         return bridge;
1121     }
1122 
1123     /**
1124      * The link between the popup window and the scenegraph.
1125      *
1126      * @since JavaFX 2.1
1127      */
1128     protected class CSSBridge extends Pane {
1129 
1130         private final PopupControl popupControl = PopupControl.this;
1131 
1132         {
1133             // To initialize the class helper at the begining each constructor of this class
1134             CSSBridgeHelper.initHelper(this);
1135         }
1136 
1137         /**
1138          * Requests a layout pass to be performed before the next scene is
1139          * rendered. This is batched up asynchronously to happen once per
1140          * "pulse", or frame of animation.
1141          * <p>
1142          * If this parent is either a layout root or unmanaged, then it will be
1143          * added directly to the scene's dirty layout list, otherwise requestLayout
1144          * will be invoked on its parent.
1145          */
1146         @Override public void requestLayout() {
1147             prefWidthCache = -1;
1148             prefHeightCache = -1;
1149             minWidthCache = -1;
1150             minHeightCache = -1;
1151             maxWidthCache = -1;
1152             maxHeightCache = -1;
1153             //skinSizeComputed = false; -- RT-33073 disabled this
1154             super.requestLayout();
1155         }
1156 
1157         /**
1158          * This method should be treated as final and should not be overridden by any subclasses of CSSBridge.
1159          * @return the styleable parent
1160          */
1161         @Override
1162         public Styleable getStyleableParent() {
1163             return PopupControl.this.getStyleableParent();
1164         }
1165 
1166         @Override
1167         public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
1168             return PopupControl.this.getCssMetaData();
1169         }
1170 
1171         /*
1172          * Note: This method MUST only be called via its accessor method.
1173          */
1174         private List<String> doGetAllParentStylesheets() {
1175             Styleable styleable = getStyleableParent();
1176             if (styleable instanceof Parent) {
1177                 return ParentHelper.getAllParentStylesheets((Parent)styleable);
1178             }
1179             return null;
1180         }
1181 
1182         /*
1183          * Note: This method MUST only be called via its accessor method.
1184          */
1185         private void doProcessCSS() {
1186             CSSBridgeHelper.superProcessCSS(this);
1187 
1188             if (getSkin() == null) {
1189                 // try to create default skin
1190                 final Skin<?> defaultSkin = createDefaultSkin();
1191                 if (defaultSkin != null) {
1192                     skinProperty().set(defaultSkin);
1193                     CSSBridgeHelper.superProcessCSS(this);
1194                 } else {
1195                     final String msg = "The -fx-skin property has not been defined in CSS for " + this +
1196                             " and createDefaultSkin() returned null.";
1197                     final List<CssParser.ParseError> errors = StyleManager.getErrors();
1198                     if (errors != null) {
1199                         CssParser.ParseError error = new CssParser.ParseError(msg);
1200                         errors.add(error); // RT-19884
1201                     }
1202                     Logging.getControlsLogger().severe(msg);
1203                 }
1204             }
1205         }
1206 
1207     }
1208 
1209     /*
1210      * Used to access internal methods of CSSBridge.
1211      */
1212     static final class CSSBridgeHelper extends PaneHelper {
1213         private static final CSSBridgeHelper theInstance;
1214 
1215         static {
1216             theInstance = new CSSBridgeHelper();
1217         }
1218 
1219         private static CSSBridgeHelper getInstance() {
1220             return theInstance;
1221         }
1222 
1223         public static void initHelper(CSSBridge cssBridge) {
1224             setHelper(cssBridge, getInstance());
1225         }
1226 
1227         public static void superProcessCSS(Node node) {
1228             ((CSSBridgeHelper) getHelper(node)).superProcessCSSImpl(node);
1229         }
1230 
1231         void superProcessCSSImpl(Node node) {
1232             super.processCSSImpl(node);
1233         }
1234 
1235         @Override
1236         protected void processCSSImpl(Node node) {
1237             ((CSSBridge) node).doProcessCSS();
1238         }
1239 
1240         @Override
1241         protected List<String> getAllParentStylesheetsImpl(Parent parent) {
1242             return ((CSSBridge) parent).doGetAllParentStylesheets();
1243         }
1244     }
1245 
1246 }