Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.scene.control;
027
028import java.lang.reflect.Constructor;
029import java.lang.reflect.InvocationTargetException;
030import java.util.ArrayList;
031import java.util.Collections;
032import java.util.List;
033
034import javafx.application.Application;
035import javafx.beans.property.DoubleProperty;
036import javafx.beans.property.DoublePropertyBase;
037import javafx.beans.property.ObjectProperty;
038import javafx.beans.property.ObjectPropertyBase;
039import javafx.beans.property.SimpleStringProperty;
040import javafx.beans.property.StringProperty;
041import javafx.collections.FXCollections;
042import javafx.collections.ListChangeListener.Change;
043import javafx.collections.ObservableList;
044import javafx.collections.ObservableSet;
045import javafx.scene.Group;
046import javafx.scene.Node;
047import javafx.scene.Parent;
048import javafx.scene.Scene;
049import javafx.stage.PopupWindow;
050import com.sun.javafx.application.PlatformImpl;
051import com.sun.javafx.collections.TrackableObservableList;
052import com.sun.javafx.css.CssError;
053import javafx.css.CssMetaData;
054import javafx.css.PseudoClass;
055import com.sun.javafx.css.StyleManager;
056import javafx.css.Styleable;
057import javafx.css.StyleableStringProperty;
058import com.sun.javafx.css.converters.StringConverter;
059import com.sun.javafx.scene.control.Logging;
060import javafx.css.StyleableProperty;
061import javafx.stage.Window;
062import sun.util.logging.PlatformLogger;
063
064/**
065 * An extension of PopupWindow that allows for CSS styling.
066 */
067public class PopupControl extends PopupWindow implements Skinnable, Styleable {
068
069    /**
070     * Sentinel value which can be passed to a control's setMinWidth(), setMinHeight(),
071     * setMaxWidth() or setMaxHeight() methods to indicate that the preferred dimension
072     * should be used for that max and/or min constraint.
073     */
074    public static final double USE_PREF_SIZE = Double.NEGATIVE_INFINITY;
075
076     /**
077      * Sentinel value which can be passed to a control's setMinWidth(), setMinHeight(),
078      * setPrefWidth(), setPrefHeight(), setMaxWidth(), setMaxHeight() methods
079      * to reset the control's size constraint back to it's intrinsic size returned
080      * by computeMinWidth(), computeMinHeight(), computePrefWidth(), computePrefHeight(),
081      * computeMaxWidth(), or computeMaxHeight().
082      */
083    public static final double USE_COMPUTED_SIZE = -1;
084
085    static {
086        // Ensures that the default application user agent stylesheet is loaded
087        if (Application.getUserAgentStylesheet() == null) {
088            PlatformImpl.setDefaultPlatformUserAgentStylesheet();
089        }
090    }
091
092    /**
093     * We need a special root node, except we can't replace the special
094     * root node already in the PopupControl. So we'll set our own
095     * special almost-root node that is a child of the root.
096     *
097     * This special root node is responsible for mapping the id, styleClass,
098     * and style defined on the PopupControl such that CSS will read the
099     * values from the PopupControl, and then apply CSS state to that
100     * special node. The node will then be able to pass impl_cssSet calls
101     * along, such that any subclass of PopupControl will be able to
102     * use the Styleable properties  and we'll be able to style it from
103     * CSS, in such a way that it participates and applies to the skin,
104     * exactly the way that normal Skin's work for normal Controls.
105     */
106    protected CSSBridge bridge;
107    
108    /**
109     * Create a new empty PopupControl.
110     */
111    public PopupControl() {
112        super();
113        this.bridge = new CSSBridge();
114
115        // Bind up these two properties. Note that the third, styleClass, is
116        // handled in the onChange listener for that list.
117        bridge.idProperty().bind(idProperty());
118        bridge.styleProperty().bind(styleProperty());
119
120        getContent().add(bridge);
121
122        // TODO the fact that PopupWindow uses a group for auto-moving things
123        // around means that the scene resize semantics don't work if the
124        // child is a resizable. I will need to replicate those semantics
125        // here sometime, such that if the Skin provides a resizable, it is
126        // given to match the popup window's width & height.
127    }
128
129    // TODO we should have some interface which has id, styleClass, style, etc
130    // which is in common between PopupControl and Node.
131    
132    // TODO we should have some interface for minWidth, maxWidth, etc which are
133    // both here and on Node.
134    
135    /**
136     * The id of this {@code PopupControl}. This simple string identifier is useful for
137     * finding a specific Node within the scene graph. While the id of a Node
138     * should be unique within the scene graph, this uniqueness is not enforced.
139     * This is analogous to the "id" attribute on an HTML element 
140     * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
141     *
142     * @defaultValue null
143     */
144    private final StringProperty id = new SimpleStringProperty(this, "id");
145    public final StringProperty idProperty() { return id; }
146    
147    /**
148     * Sets 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     * @param value  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    public final void setId(String value) { id.set(value); }
159    
160    /**
161     * The id of this {@code PopupControl}. This simple string identifier is useful for
162     * finding a specific Node within the scene graph. While the id of a Node
163     * should be unique within the scene graph, this uniqueness is not enforced.
164     * This is analogous to the "id" attribute on an HTML element 
165     * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
166     *
167     * @return the id assigned to this {@code PopupControl} using the {@code setId} 
168     *         method or {@code null}, if no id has been assigned.
169     * @defaultValue null
170     */
171    @Override public final String getId() { return id.get(); }
172
173    /**
174     * A list of String identifiers which can be used to logically group
175     * Nodes, specifically for an external style engine. This variable is
176     * analogous to the "class" attribute on an HTML element and, as such,
177     * each element of the list is a style class to which this Node belongs.
178     *
179     * @see <a href="http://www.w3.org/TR/css3-selectors/#class-html">CSS3 class selectors</a>
180     * @defaultValue null
181     */
182    private final ObservableList<String> styleClass = new TrackableObservableList<String>() {
183        // TODO we can save one class if we factor this into a StyleClassList, and reuse it between
184        // here and in Node.
185        @Override protected void onChanged(Change<String> c) {
186            // Push the change along to the bridge group
187            bridge.getStyleClass().setAll(styleClass);
188        }
189
190        @Override public String toString() {
191            if (size() == 0) {
192                return "";
193            } else if (size() == 1) {
194                return get(0);
195            } else {
196                StringBuilder buf = new StringBuilder();
197                for (int i = 0; i < size(); i++) {
198                    buf.append(get(i));
199                    if (i + 1 < size()) {
200                        buf.append(' ');
201                    }
202                }
203                return buf.toString();
204            }
205        }
206    };
207
208    /**
209     * Returns the list of String identifiers that make up the styleClass
210     * for this PopupControl. 
211     */
212    @Override public final ObservableList<String> getStyleClass() { return styleClass; }
213
214    /**
215     * A string representation of the CSS style associated with this
216     * specific PopupControl. This is analogous to the "style" attribute of an
217     * HTML element. Note that, like the HTML style attribute, this
218     * variable contains style properties and values and not the
219     * selector portion of a style rule.
220     * <p>
221     * Parsing this style might not be supported on some limited
222     * platforms. It is recommended to use a standalone CSS file instead.
223     *
224     * @defaultValue empty string
225     */
226    private final StringProperty style = new SimpleStringProperty(this, "style");
227    
228    /**
229     * A string representation of the CSS style associated with this
230     * specific {@code PopupControl}. This is analogous to the "style" attribute of an
231     * HTML element. Note that, like the HTML style attribute, this
232     * variable contains style properties and values and not the
233     * selector portion of a style rule.
234     * @param value The inline CSS style to use for this {@code PopupControl}.
235     *         {@code null} is implicitly converted to an empty String. 
236     * @defaultValue empty string
237     */
238    public final void setStyle(String value) { style.set(value); }
239    
240    // TODO: javadoc copied from property for the sole purpose of providing a return tag
241    /**
242     * A string representation of the CSS style associated with this
243     * specific {@code PopupControl}. This is analogous to the "style" attribute of an
244     * HTML element. Note that, like the HTML style attribute, this
245     * variable contains style properties and values and not the
246     * selector portion of a style rule.
247     * @defaultValue empty string
248     * @return The inline CSS style associated with this {@code PopupControl}.
249     *         If this {@code PopupControl} does not have an inline style,
250     *         an empty String is returned.
251     */
252    @Override public final String getStyle() { return style.get(); }
253    public final StringProperty styleProperty() { return style; }
254
255    @Override public final ObjectProperty<Skin<?>> skinProperty() { 
256        return bridge.skin; }
257    @Override public final void setSkin(Skin<?> value) { 
258        skinProperty().set(value); 
259    }
260    @Override public final Skin<?> getSkin() {
261        return skinProperty().getValue();
262    }
263
264    /**
265     * Gets the Skin's node, or returns null if there is no Skin.
266     * Convenience method for getting the node of the skin. This is null-safe,
267     * meaning if skin is null then it will return null instead of throwing
268     * a NullPointerException.
269     *
270     * @return The Skin's node, or null.
271     */
272    private Node getSkinNode() {
273        return getSkin() == null ? null : getSkin().getNode();
274    }
275
276    /**
277     * Property for overriding the control's computed minimum width.
278     * This should only be set if the control's internally computed minimum width
279     * doesn't meet the application's layout needs.
280     * <p>
281     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
282     * <code>getMinWidth(forHeight)</code> will return the control's internally
283     * computed minimum width.
284     * <p>
285     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
286     * <code>getMinWidth(forHeight)</code> to return the control's preferred width,
287     * enabling applications to easily restrict the resizability of the control.
288     */
289    private DoubleProperty minWidth;
290    
291    /**
292     * Property for overriding the control's computed minimum width.
293     * This should only be set if the control's internally computed minimum width
294     * doesn't meet the application's layout needs.
295     * <p>
296     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
297     * <code>getMinWidth(forHeight)</code> will return the control's internally
298     * computed minimum width.
299     * <p>
300     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
301     * <code>getMinWidth(forHeight)</code> to return the control's preferred width,
302     * enabling applications to easily restrict the resizability of the control.
303     */
304    public final void setMinWidth(double value) { minWidthProperty().set(value); }
305    
306    /**
307     * Property for overriding the control's computed minimum width.
308     * This should only be set if the control's internally computed minimum width
309     * doesn't meet the application's layout needs.
310     * <p>
311     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
312     * <code>getMinWidth(forHeight)</code> will return the control's internally
313     * computed minimum width.
314     * <p>
315     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
316     * <code>getMinWidth(forHeight)</code> to return the control's preferred width,
317     * enabling applications to easily restrict the resizability of the control.
318     */
319    public final double getMinWidth() { return minWidth == null ? USE_COMPUTED_SIZE : minWidth.get(); }
320    public final DoubleProperty minWidthProperty() {
321        if (minWidth == null) {
322            minWidth = new DoublePropertyBase(USE_COMPUTED_SIZE) {
323                @Override public void invalidated() {
324                    if (isShowing()) bridge.requestLayout();
325                }
326
327                @Override
328                public Object getBean() {
329                    return PopupControl.this;
330                }
331
332                @Override
333                public String getName() {
334                    return "minWidth";
335                }
336            };
337        }
338        return minWidth;
339    }
340
341
342    /**
343     * Property for overriding the control's computed minimum height.
344     * This should only be set if the control's internally computed minimum height
345     * doesn't meet the application's layout needs.
346     * <p>
347     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
348     * <code>getMinHeight(forWidth)</code> will return the control's internally
349     * computed minimum height.
350     * <p>
351     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
352     * <code>getMinHeight(forWidth)</code> to return the control's preferred height,
353     * enabling applications to easily restrict the resizability of the control.
354     *
355     */
356    private DoubleProperty minHeight;
357    
358    /**
359     * Property for overriding the control's computed minimum height.
360     * This should only be set if the control's internally computed minimum height
361     * doesn't meet the application's layout needs.
362     * <p>
363     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
364     * <code>getMinHeight(forWidth)</code> will return the control's internally
365     * computed minimum height.
366     * <p>
367     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
368     * <code>getMinHeight(forWidth)</code> to return the control's preferred height,
369     * enabling applications to easily restrict the resizability of the control.
370     *
371     */
372    public final void setMinHeight(double value) { minHeightProperty().set(value); }
373    
374    /**
375     * Property for overriding the control's computed minimum height.
376     * This should only be set if the control's internally computed minimum height
377     * doesn't meet the application's layout needs.
378     * <p>
379     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
380     * <code>getMinHeight(forWidth)</code> will return the control's internally
381     * computed minimum height.
382     * <p>
383     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
384     * <code>getMinHeight(forWidth)</code> to return the control's preferred height,
385     * enabling applications to easily restrict the resizability of the control.
386     *
387     */
388    public final double getMinHeight() { return minHeight == null ? USE_COMPUTED_SIZE : minHeight.get(); }
389    public final DoubleProperty minHeightProperty() {
390        if (minHeight == null) {
391            minHeight = new DoublePropertyBase(USE_COMPUTED_SIZE) {
392                @Override public void invalidated() {
393                    if (isShowing()) bridge.requestLayout();
394                }
395
396                @Override
397                public Object getBean() {
398                    return PopupControl.this;
399                }
400
401                @Override
402                public String getName() {
403                    return "minHeight";
404                }
405            };
406        }
407        return minHeight;
408    }
409
410    /**
411     * Convenience method for overriding the control's computed minimum width and height.
412     * This should only be called if the control's internally computed minimum size
413     * doesn't meet the application's layout needs.
414     *
415     * @see #setMinWidth
416     * @see #setMinHeight
417     * @param minWidth  the override value for minimum width
418     * @param minHeight the override value for minimum height
419     */
420    public void setMinSize(double minWidth, double minHeight) {
421        setMinWidth(minWidth);
422        setMinHeight(minHeight);
423    }
424
425    /**
426     * Property for overriding the control's computed preferred width.
427     * This should only be set if the control's internally computed preferred width
428     * doesn't meet the application's layout needs.
429     * <p>
430     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
431     * <code>getPrefWidth(forHeight)</code> will return the control's internally
432     * computed preferred width.
433     */
434    private DoubleProperty prefWidth;
435    
436    /**
437     * Property for overriding the control's computed preferred width.
438     * This should only be set if the control's internally computed preferred width
439     * doesn't meet the application's layout needs.
440     * <p>
441     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
442     * <code>getPrefWidth(forHeight)</code> will return the control's internally
443     * computed preferred width.
444     */
445    public final void setPrefWidth(double value) { prefWidthProperty().set(value); }
446    
447    /**
448     * Property for overriding the control's computed preferred width.
449     * This should only be set if the control's internally computed preferred width
450     * doesn't meet the application's layout needs.
451     * <p>
452     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
453     * <code>getPrefWidth(forHeight)</code> will return the control's internally
454     * computed preferred width.
455     */
456    public final double getPrefWidth() { return prefWidth == null ? USE_COMPUTED_SIZE : prefWidth.get(); }
457    public final DoubleProperty prefWidthProperty() {
458        if (prefWidth == null) {
459            prefWidth = new DoublePropertyBase(USE_COMPUTED_SIZE) {
460                @Override public void invalidated() {
461                    if (isShowing()) bridge.requestLayout();
462                }
463
464                @Override
465                public Object getBean() {
466                    return PopupControl.this;
467                }
468
469                @Override
470                public String getName() {
471                    return "prefWidth";
472                }
473            };
474        }
475        return prefWidth;
476    }
477
478    /**
479     * Property for overriding the control's computed preferred height.
480     * This should only be set if the control's internally computed preferred height
481     * doesn't meet the application's layout needs.
482     * <p>
483     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
484     * <code>getPrefHeight(forWidth)</code> will return the control's internally
485     * computed preferred width.
486     *
487     */
488    private DoubleProperty prefHeight;
489    
490    /**
491     * Property for overriding the control's computed preferred height.
492     * This should only be set if the control's internally computed preferred height
493     * doesn't meet the application's layout needs.
494     * <p>
495     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
496     * <code>getPrefHeight(forWidth)</code> will return the control's internally
497     * computed preferred width.
498     *
499     */
500    public final void setPrefHeight(double value) { prefHeightProperty().set(value); }
501    
502    /**
503     * Property for overriding the control's computed preferred height.
504     * This should only be set if the control's internally computed preferred height
505     * doesn't meet the application's layout needs.
506     * <p>
507     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
508     * <code>getPrefHeight(forWidth)</code> will return the control's internally
509     * computed preferred width.
510     *
511     */
512    public final double getPrefHeight() { return prefHeight == null ? USE_COMPUTED_SIZE : prefHeight.get(); }
513    public final DoubleProperty prefHeightProperty() {
514        if (prefHeight == null) {
515            prefHeight = new DoublePropertyBase(USE_COMPUTED_SIZE) {
516                @Override public void invalidated() {
517                    if (isShowing()) bridge.requestLayout();
518                }
519
520                @Override
521                public Object getBean() {
522                    return PopupControl.this;
523                }
524
525                @Override
526                public String getName() {
527                    return "prefHeight";
528                }
529            };
530        }
531        return prefHeight;
532    }
533
534    /**
535     * Convenience method for overriding the control's computed preferred width and height.
536     * This should only be called if the control's internally computed preferred size
537     * doesn't meet the application's layout needs.
538     *
539     * @see #setPrefWidth
540     * @see #setPrefHeight
541     * @param prefWidth the override value for preferred width
542     * @param prefHeight the override value for preferred height
543     */
544    public void setPrefSize(double prefWidth, double prefHeight) {
545        setPrefWidth(prefWidth);
546        setPrefHeight(prefHeight);
547    }
548
549    /**
550     * Property for overriding the control's computed maximum width.
551     * This should only be set if the control's internally computed maximum width
552     * doesn't meet the application's layout needs.
553     * <p>
554     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
555     * <code>getMaxWidth(forHeight)</code> will return the control's internally
556     * computed maximum width.
557     * <p>
558     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
559     * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
560     * enabling applications to easily restrict the resizability of the control.
561     */
562    private DoubleProperty maxWidth;
563    
564    /**
565     * Property for overriding the control's computed maximum width.
566     * This should only be set if the control's internally computed maximum width
567     * doesn't meet the application's layout needs.
568     * <p>
569     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
570     * <code>getMaxWidth(forHeight)</code> will return the control's internally
571     * computed maximum width.
572     * <p>
573     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
574     * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
575     * enabling applications to easily restrict the resizability of the control.
576     */
577    public final void setMaxWidth(double value) { maxWidthProperty().set(value); }
578    
579    /**
580     * Property for overriding the control's computed maximum width.
581     * This should only be set if the control's internally computed maximum width
582     * doesn't meet the application's layout needs.
583     * <p>
584     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
585     * <code>getMaxWidth(forHeight)</code> will return the control's internally
586     * computed maximum width.
587     * <p>
588     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
589     * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
590     * enabling applications to easily restrict the resizability of the control.
591     */
592    public final double getMaxWidth() { return maxWidth == null ? USE_COMPUTED_SIZE : maxWidth.get(); }
593    public final DoubleProperty maxWidthProperty() {
594        if (maxWidth == null) {
595            maxWidth = new DoublePropertyBase(USE_COMPUTED_SIZE) {
596                @Override public void invalidated() {
597                    if (isShowing()) bridge.requestLayout();
598                }
599
600                @Override
601                public Object getBean() {
602                    return PopupControl.this;
603                }
604
605                @Override
606                public String getName() {
607                    return "maxWidth";
608                }
609            };
610        }
611        return maxWidth;
612    }
613
614    /**
615     * Property for overriding the control's computed maximum height.
616     * This should only be set if the control's internally computed maximum height
617     * doesn't meet the application's layout needs.
618     * <p>
619     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
620     * <code>getMaxHeight(forWidth)</code> will return the control's internally
621     * computed maximum height.
622     * <p>
623     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
624     * <code>getMaxHeight(forWidth)</code> to return the control's preferred height,
625     * enabling applications to easily restrict the resizability of the control.
626     *
627     */
628    private DoubleProperty maxHeight;
629    
630    /**
631     * Property for overriding the control's computed maximum height.
632     * This should only be set if the control's internally computed maximum height
633     * doesn't meet the application's layout needs.
634     * <p>
635     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
636     * <code>getMaxHeight(forWidth)</code> will return the control's internally
637     * computed maximum height.
638     * <p>
639     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
640     * <code>getMaxHeight(forWidth)</code> to return the control's preferred height,
641     * enabling applications to easily restrict the resizability of the control.
642     *
643     */
644    public final void setMaxHeight(double value) { maxHeightProperty().set(value); }
645    
646    /**
647     * Property for overriding the control's computed maximum height.
648     * This should only be set if the control's internally computed maximum height
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>getMaxHeight(forWidth)</code> will return the control's internally
653     * computed maximum height.
654     * <p>
655     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
656     * <code>getMaxHeight(forWidth)</code> to return the control's preferred height,
657     * enabling applications to easily restrict the resizability of the control.
658     *
659     */
660    public final double getMaxHeight() { return maxHeight == null ? USE_COMPUTED_SIZE : maxHeight.get(); }
661    public final DoubleProperty maxHeightProperty() {
662        if (maxHeight == null) {
663            maxHeight = new DoublePropertyBase(USE_COMPUTED_SIZE) {
664                @Override public void invalidated() {
665                    if (isShowing()) bridge.requestLayout();
666                }
667
668                @Override
669                public Object getBean() {
670                    return PopupControl.this;
671                }
672
673                @Override
674                public String getName() {
675                    return "maxHeight";
676                }
677            };
678        }
679        return maxHeight;
680    }
681
682    /**
683     * Convenience method for overriding the control's computed maximum width and height.
684     * This should only be called if the control's internally computed maximum size
685     * doesn't meet the application's layout needs.
686     *
687     * @see #setMaxWidth
688     * @see #setMaxHeight
689     * @param maxWidth  the override value for maximum width
690     * @param maxHeight the override value for maximum height
691     */
692    public void setMaxSize(double maxWidth, double maxHeight) {
693        setMaxWidth(maxWidth);
694        setMaxHeight(maxHeight);
695    }
696
697    /**
698     * Cached prefWidth, prefHeight, minWidth, minHeight. These
699     * results are repeatedly sought during the layout pass,
700     * and caching the results leads to a significant decrease
701     * in overhead.
702     */
703    private double prefWidthCache = -1;
704    private double prefHeightCache = -1;
705    private double minWidthCache = -1;
706    private double minHeightCache = -1;
707    private double maxWidthCache = -1;
708    private double maxHeightCache = -1;
709    private boolean skinSizeComputed = false;
710    
711    /**
712     * Called during layout to determine the minimum width for this node.
713     * Returns the value from <code>minWidth(forHeight)</code> unless
714     * the application overrode the minimum width by setting the minWidth property.
715     *
716     * @param height the height
717     * @see #setMinWidth
718     * @return the minimum width that this node should be resized to during layout
719     */
720    public final double minWidth(double height) {
721        double override = getMinWidth();
722        if (override == USE_COMPUTED_SIZE) {
723            if (minWidthCache == -1) minWidthCache = recalculateMinWidth(height);
724            return minWidthCache;
725        } else if (override == USE_PREF_SIZE) {
726            return prefWidth(height);
727        }
728        return override;
729    }
730
731    /**
732     * Called during layout to determine the minimum height for this node.
733     * Returns the value from <code>minHeight(forWidth)</code> unless
734     * the application overrode the minimum height by setting the minHeight property.
735     *
736     * @param width The width
737     * @see #setMinHeight
738     * @return the minimum height that this node should be resized to during layout
739     */
740    public final double minHeight(double width) {
741        double override = getMinHeight();
742        if (override == USE_COMPUTED_SIZE) {
743            if (minHeightCache == -1) minHeightCache = recalculateMinHeight(width);
744            return minHeightCache;
745        } else if (override == USE_PREF_SIZE) {
746            return prefHeight(width);
747        }
748        return override;
749    }
750
751
752    /**
753     * Called during layout to determine the preferred width for this node.
754     * Returns the value from <code>prefWidth(forHeight)</code> unless
755     * the application overrode the preferred width by setting the prefWidth property.
756     *
757     * @param height the height
758     * @see #setPrefWidth
759     * @return the preferred width that this node should be resized to during layout
760     */
761    public final double prefWidth(double height) {
762        double override = getPrefWidth();
763        if (override == USE_COMPUTED_SIZE) {
764            if (prefWidthCache == -1) prefWidthCache = recalculatePrefWidth(height);
765            return prefWidthCache;
766        } else if (override == USE_PREF_SIZE) {
767            return prefWidth(height);
768        }
769        return override;
770    }
771
772    /**
773     * Called during layout to determine the preferred height for this node.
774     * Returns the value from <code>prefHeight(forWidth)</code> unless
775     * the application overrode the preferred height by setting the prefHeight property.
776     *
777     * @param width the width
778     * @see #setPrefHeight
779     * @return the preferred height that this node should be resized to during layout
780     */
781    public final double prefHeight(double width) {
782        double override = getPrefHeight();
783        if (override == USE_COMPUTED_SIZE) {
784            if (prefHeightCache == -1) prefHeightCache = recalculatePrefHeight(width);
785            return prefHeightCache;
786        } else if (override == USE_PREF_SIZE) {
787            return prefHeight(width);
788        }
789        return override;
790    }
791
792    /**
793     * Called during layout to determine the maximum width for this node.
794     * Returns the value from <code>maxWidth(forHeight)</code> unless
795     * the application overrode the maximum width by setting the maxWidth property.
796     *
797     * @param height the height
798     * @see #setMaxWidth
799     * @return the maximum width that this node should be resized to during layout
800     */
801    public final double maxWidth(double height) {
802        double override = getMaxWidth();
803        if (override == USE_COMPUTED_SIZE) {
804            if (maxWidthCache == -1) maxWidthCache = recalculateMaxWidth(height);
805            return maxWidthCache;
806        } else if (override == USE_PREF_SIZE) {
807            return prefWidth(height);
808        }
809        return override;
810    }
811
812    /**
813     * Called during layout to determine the maximum height for this node.
814     * Returns the value from <code>maxHeight(forWidth)</code> unless
815     * the application overrode the maximum height by setting the maxHeight property.
816     *
817     * @param width the width
818     * @see #setMaxHeight
819     * @return the maximum height that this node should be resized to during layout
820     */
821    public final double maxHeight(double width) {
822        double override = getMaxHeight();
823        if (override == USE_COMPUTED_SIZE) {
824            if (maxHeightCache == -1) maxHeightCache = recalculateMaxHeight(width);
825            return maxHeightCache;
826        } else if (override == USE_PREF_SIZE) {
827            return prefHeight(width);
828        }
829        return override;
830    }
831
832    // Implementation of the Resizable interface.
833    // Because only the skin can know the min, pref, and max sizes, these
834    // functions are implemented to delegate to skin. If there is no skin then
835    // we simply return 0 for all the values since a Control without a Skin
836    // doesn't render
837    private double recalculateMinWidth(double height) {
838        recomputeSkinSize();
839        return getSkinNode() == null ? 0 : getSkinNode().minWidth(height);
840    }
841    private double recalculateMinHeight(double width) {
842        recomputeSkinSize();
843        return getSkinNode() == null ? 0 : getSkinNode().minHeight(width);
844    }
845    private double recalculateMaxWidth(double height) {
846        recomputeSkinSize();
847        return getSkinNode() == null ? 0 : getSkinNode().maxWidth(height);
848    }
849    private double recalculateMaxHeight(double width) {
850        recomputeSkinSize();
851        return getSkinNode() == null ? 0 : getSkinNode().maxHeight(width);
852    }
853    private double recalculatePrefWidth(double height) {
854        recomputeSkinSize();
855        return getSkinNode() == null? 0 : getSkinNode().prefWidth(height);
856    }
857    private double recalculatePrefHeight(double width) {
858        recomputeSkinSize();
859        return getSkinNode() == null? 0 : getSkinNode().prefHeight(width);
860    }
861    
862    private void recomputeSkinSize() {
863        if (!skinSizeComputed && getOwnerWindow() != null) { //getScene() != null && getScene().getRoot() != null) {
864            // RT-14094, RT-16754: We need the skins of the popup
865            // and it children before the stage is visible so we
866            // can calculate the popup position based on content
867            // size.
868            getScene().getRoot().impl_processCSS(true);
869            skinSizeComputed = true;
870        }
871    }
872//    public double getBaselineOffset() { return getSkinNode() == null? 0 : getSkinNode().getBaselineOffset(); }
873
874    /**
875     * Create a new instance of the default skin for this control. This is called to create a skin for the control if
876     * no skin is provided via CSS {@code -fx-skin} or set explicitly in a sub-class with {@code  setSkin(...)}.
877     *
878     * @return  new instance of default skin for this control. If null then the control will have no skin unless one
879     *          is provided by css.
880     */
881    protected Skin<?> createDefaultSkin() {
882        return null;
883    }
884
885    /***************************************************************************
886     *                                                                         *
887     *                         StyleSheet Handling                             *
888     *                                                                         *
889     **************************************************************************/
890
891    private static class StyleableProperties {
892        private static final CssMetaData<CSSBridge,String> SKIN = 
893            new CssMetaData<CSSBridge,String>("-fx-skin",
894                StringConverter.getInstance()) {
895
896            @Override
897            public boolean isSettable(CSSBridge n) {
898                return n.skin == null || !n.skin.isBound();
899            }
900
901            @Override
902            public StyleableProperty<String> getStyleableProperty(CSSBridge n) {
903                return (StyleableProperty)n.skinClassNameProperty();
904            }
905        };
906
907        private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
908        static {
909            final List<CssMetaData<? extends Styleable, ?>> styleables =
910                new ArrayList<CssMetaData<? extends Styleable, ?>>();
911            Collections.addAll(styleables,
912                SKIN
913            );
914            STYLEABLES = Collections.unmodifiableList(styleables);
915        }
916    }
917    
918    /**
919     * @return The CssMetaData associated with this class, which may include the
920     * CssMetaData of its super classes.
921     */
922    public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
923        return StyleableProperties.STYLEABLES;
924    }
925
926    /**
927     * This method should delegate to {@link Node#getClassCssMetaData()} so that
928     * a Node's CssMetaData can be accessed without the need for reflection.
929     * @return The CssMetaData associated with this node, which may include the
930     * CssMetaData of its super classes.
931     */
932    public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
933        return getClassCssMetaData();
934    }
935
936    /**
937     * @see Node#pseudoClassStateChanged(javafx.css.PseudoClass, boolean) 
938     */
939    public final void pseudoClassStateChanged(PseudoClass pseudoClass, boolean active) {
940        bridge.pseudoClassStateChanged(pseudoClass, active);
941    }
942    
943    /**
944     * {@inheritDoc}
945     * @return "PopupControl"
946     */
947    @Override
948    public String getTypeSelector() {
949        return "PopupControl";
950    }
951    
952    /**
953     * {@inheritDoc}
954     *
955     * A PopupControl&apos;s styles are based on the popup &quot;owner&quot; which is the
956     * {@link javafx.stage.PopupWindow#getOwnerNode() ownerNode} or,
957     * if the ownerNode is not set, the root of the {@link javafx.stage.PopupWindow#getOwnerWindow() ownerWindow&apos;s}
958     * scene. If the popup has not been shown, both ownerNode and ownerWindow will be null and {@code null} will be returned.
959     *
960     * Note that the PopupWindow&apos;s scene root is not returned because there is no way to guarantee that the
961     * PopupWindow&apos;s scene root would properly return the ownerNode or ownerWindow.
962     *
963     * @return {@link javafx.stage.PopupWindow#getOwnerNode()}, {@link javafx.stage.PopupWindow#getOwnerWindow()},
964     * or null.
965     */
966    @Override
967    public Styleable getStyleableParent() {
968
969        final Node ownerNode = getOwnerNode();
970        if (ownerNode != null) {
971
972            return ownerNode;
973
974        } else {
975
976            final Window ownerWindow = getOwnerWindow();
977            if (ownerWindow != null) {
978
979                final Scene ownerScene = ownerWindow.getScene();
980                if (ownerScene != null) {
981
982                    return ownerScene.getRoot();
983                }
984            }
985        }
986
987        return null;
988    }
989
990    /**
991     * {@inheritDoc}
992     */
993    public final ObservableSet<PseudoClass> getPseudoClassStates() {
994        return FXCollections.emptyObservableSet();
995    }
996
997    /**
998     * @treatAsPrivate implementation detail
999     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
1000     */
1001    @Deprecated    
1002   // SB-dependency: RT-21094 has been filed to track this
1003   public Node impl_styleableGetNode() {
1004        return bridge;
1005    }
1006   
1007    protected class CSSBridge extends Group {
1008        private String currentSkinClassName = null;
1009        
1010        /**
1011         * {@inheritDoc}
1012         */
1013        public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
1014            // see RT-19263
1015            return PopupControl.this.getCssMetaData();
1016        }
1017
1018        /**
1019         * Requests a layout pass to be performed before the next scene is
1020         * rendered. This is batched up asynchronously to happen once per
1021         * "pulse", or frame of animation.
1022         * <p/>
1023         * If this parent is either a layout root or unmanaged, then it will be
1024         * added directly to the scene's dirty layout list, otherwise requestLayout
1025         * will be invoked on its parent.
1026         */
1027        @Override public void requestLayout() {
1028            prefWidthCache = -1;
1029            prefHeightCache = -1;
1030            minWidthCache = -1;
1031            minHeightCache = -1;
1032            maxWidthCache = -1;
1033            maxHeightCache = -1;
1034            skinSizeComputed = false;
1035            super.requestLayout();
1036        }
1037
1038        /**
1039        * Skin is responsible for rendering this {@code PopupControl}. From the
1040        * perspective of the {@code PopupControl}, the {@code Skin} is a black box.
1041        * It listens and responds to changes in state in a {@code PopupControl}.
1042        * <p>
1043        * There is a one-to-one relationship between a {@code PopupControl} and its
1044        * {@code Skin}. Every {@code Skin} maintains a back reference to the
1045        * {@code PopupControl}.
1046        * <p>
1047        * A skin may be null.
1048        */
1049        private ObjectProperty<Skin<?>> skin = new ObjectPropertyBase<Skin<?>>() {
1050            // We store a reference to the oldValue so that we can handle
1051            // changes in the skin properly in the case of binding. This is
1052            // only needed because invalidated() does not currently take
1053            // a reference to the old value.
1054            private Skin<?> oldValue;
1055
1056            @Override
1057            public void set(Skin<?> v) {
1058
1059                if (v == null 
1060                    ? oldValue == null
1061                    : oldValue != null && v.getClass().equals(oldValue.getClass()))
1062                    return;
1063
1064                super.set(v);
1065
1066                // Collect the name of the currently installed skin class. We do this
1067                // so that subsequent updates from CSS to the same skin class will not
1068                // result in reinstalling the skin
1069                currentSkinClassName = v == null ? null : v.getClass().getName();
1070
1071                // if someone calls setSkin, we need to make it look like they 
1072                // called set on skinClassName in order to keep CSS from overwriting
1073                // the skin. 
1074                skinClassNameProperty().set(currentSkinClassName);
1075
1076            }
1077            
1078            @Override protected void invalidated() {
1079                // Let CSS know that this property has been manually changed
1080                // Dispose of the old skin
1081                if (oldValue != null) oldValue.dispose();
1082                // Get the new value, and save it off as the new oldValue
1083                oldValue = getValue();
1084                
1085                prefWidthCache = -1;
1086                prefHeightCache = -1;
1087                minWidthCache = -1;
1088                minHeightCache = -1;
1089                maxWidthCache = -1;
1090                maxHeightCache = -1;
1091                skinSizeComputed = false;
1092                final Node n = getSkinNode();
1093                if (n != null) bridge.getChildren().setAll(n);
1094                else bridge.getChildren().clear();
1095
1096                // calling impl_reapplyCSS() as the styleable properties may now
1097                // be different, as we will now be able to return styleable properties 
1098                // belonging to the skin. If impl_reapplyCSS() is not called, the 
1099                // getCssMetaData() method is never called, so the 
1100                // skin properties are never exposed.
1101                impl_reapplyCSS();
1102                
1103                // DEBUG: Log that we've changed the skin
1104                final PlatformLogger logger = Logging.getControlsLogger();
1105                if (logger.isLoggable(PlatformLogger.FINEST)) {
1106                    logger.finest("Stored skin[" + getValue() + "] on " + this);
1107                }
1108            }
1109
1110            @Override
1111            public Object getBean() {
1112                return PopupControl.CSSBridge.this;
1113            }
1114
1115            @Override
1116            public String getName() {
1117                return "skin";
1118            }
1119        };
1120        
1121        /**
1122        * Keeps a reference to the name of the class currently acting as the skin.
1123        */
1124        private StringProperty skinClassName = null;
1125        private StringProperty skinClassNameProperty() {
1126            if (skinClassName == null) {
1127                skinClassName = new StyleableStringProperty() {
1128                    
1129                    @Override
1130                    public void set(String v) {
1131                        // do not allow the skin to be set to null through CSS
1132                        if (v == null || v.isEmpty() || v.equals(get())) return;
1133                        super.set(v);
1134                    }
1135                    
1136                    @Override
1137                    public void invalidated() {
1138
1139                        //
1140                        // if the current skin is not null, then 
1141                        // see if then check to see if the current skin's class name
1142                        // is the same as skinClassName. If it is, then there is
1143                        // no need to load the skin class. Note that the only time 
1144                        // this would be the case is if someone called setSkin since
1145                        // the skin would be set ahead of the skinClassName 
1146                        // (skinClassName is set from the skinProperty's invalidated
1147                        // method, so the skin would be set, then the skinClassName).
1148                        // If the skinClassName is set first (via CSS), then this
1149                        // invalidated method won't get called unless the value
1150                        // has changed (thus, we won't reload the same skin).
1151                        // 
1152                        if (get() != null) {
1153                            if (!get().equals(currentSkinClassName)) {
1154                                loadSkinClass();
1155                            }
1156                        // CSS should not set skin to null
1157    //                    } else {
1158    //                        setSkin(null);
1159                        }
1160                    }
1161
1162                    @Override
1163                    public Object getBean() {
1164                        return PopupControl.CSSBridge.this;
1165                    }
1166
1167                    @Override
1168                    public String getName() {
1169                        return "skinClassName";
1170                    }
1171
1172                    @Override
1173                    public CssMetaData<CSSBridge,String> getCssMetaData() {
1174                        return StyleableProperties.SKIN;
1175                    }
1176
1177                };
1178            }
1179            return skinClassName;
1180        }   
1181        
1182        protected void setSkinClassName(String skinClassName) {
1183            skinClassNameProperty().set(skinClassName);        
1184        }
1185
1186        private void loadSkinClass() {
1187            if (skinClassName == null
1188                || skinClassName.get() == null 
1189                || skinClassName.get().isEmpty()) {
1190                final String msg = 
1191                    "Empty -fx-skin property specified for popup control " + this;   
1192                final List<CssError> errors = StyleManager.getErrors();
1193                if (errors != null) {
1194                    CssError error = new CssError(msg);
1195                    errors.add(error); // RT-19884
1196                } 
1197                Logging.getControlsLogger().severe(msg);
1198                return;
1199            }
1200
1201            try {
1202                final Class<?> skinClass = Control.loadClass(skinClassName.get(), this);
1203                Constructor<?>[] constructors = skinClass.getConstructors();
1204                Constructor<?> skinConstructor = null;
1205                for (Constructor<?> c : constructors) {
1206                    Class<?>[] parameterTypes = c.getParameterTypes();
1207                    if (parameterTypes.length == 1 && PopupControl.class.isAssignableFrom(parameterTypes[0])) {
1208                        skinConstructor = c;
1209                        break;
1210                    }
1211                }
1212
1213                if (skinConstructor == null) {
1214                    final String msg = 
1215                        "No valid constructor defined in '" + skinClassName 
1216                        + "' for popup control " + this 
1217                        + ".\r\nYou must provide a constructor that accepts a single "
1218                        + "PopupControl parameter in " + skinClassName + ".";
1219                    final List<CssError> errors = StyleManager.getErrors();
1220                    if (errors != null) {
1221                        CssError error = new CssError(msg);
1222                        errors.add(error); // RT-19884
1223                    } 
1224                    Logging.getControlsLogger().severe(msg);
1225                    return;
1226                } else {
1227                    Skin<?> skinInstance = (Skin<?>) skinConstructor.newInstance(PopupControl.this);
1228                    // Do not call setSkin here since it has the side effect of
1229                    // also setting the skinClassName!
1230                    skinProperty().set(skinInstance);
1231                }
1232
1233            } catch (InvocationTargetException e) {
1234                final String msg = 
1235                    "Failed to load skin '" + skinClassName 
1236                    + "' for popup control " + this;
1237                final List<CssError> errors = StyleManager.getErrors();
1238                if (errors != null) {
1239                    CssError error = new CssError(msg + " :" + e.getLocalizedMessage());
1240                    errors.add(error); // RT-19884
1241                } 
1242                Logging.getControlsLogger().severe(msg, e.getCause());
1243            } catch (Exception e) {
1244                final String msg = 
1245                    "Failed to load skin '" + skinClassName 
1246                    + "' for popup control " + this;
1247                final List<CssError> errors = StyleManager.getErrors();
1248                if (errors != null) {
1249                    CssError error = new CssError(msg + " :" + e.getLocalizedMessage());
1250                    errors.add(error); // RT-19884
1251                } 
1252                Logging.getControlsLogger().severe(msg, e.getCause());
1253            }
1254        }
1255
1256        /**
1257         * @treatAsPrivate implementation detail
1258         * @deprecated This is an internal API that is not intended for use and will be removed in the next version
1259         */
1260        @Deprecated
1261        @Override protected void impl_processCSS() {
1262            super.impl_processCSS();
1263
1264            if (getSkin() == null) {
1265                // try to create default skin
1266                final Skin<?> defaultSkin = createDefaultSkin();
1267                if (defaultSkin != null) {
1268                    skinProperty().set(defaultSkin);
1269                    // we have to reapply css again so that the newly set skin gets css applied as well.
1270                    super.impl_processCSS();
1271                } else {
1272                    final String msg = "The -fx-skin property has not been defined in CSS for " + this +
1273                            " and createDefaultSkin() returned null.";
1274                    final List<CssError> errors = StyleManager.getErrors();
1275                    if (errors != null) {
1276                        CssError error = new CssError(msg);
1277                        errors.add(error); // RT-19884
1278                    }
1279                    Logging.getControlsLogger().severe(msg);
1280                }
1281            }
1282        }
1283
1284
1285        @Override
1286        public Styleable getStyleableParent() {
1287            return PopupControl.this.getStyleableParent();
1288        }
1289
1290        @Override public List<String> impl_getAllParentStylesheets() {
1291            Styleable styleable = getStyleableParent();
1292            if (styleable instanceof Parent) {
1293                return ((Parent)styleable).impl_getAllParentStylesheets();
1294            }
1295            return null;
1296        }
1297    }
1298
1299}