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's styles are based on the popup "owner" 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'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's scene root is not returned because there is no way to guarantee that the 961 * PopupWindow'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}