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