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; 033import javafx.application.Application; 034import javafx.beans.property.ObjectProperty; 035import javafx.beans.property.ObjectPropertyBase; 036import javafx.beans.property.SimpleObjectProperty; 037import javafx.beans.property.StringProperty; 038import javafx.collections.ObservableList; 039import javafx.event.EventHandler; 040import javafx.geometry.Insets; 041import javafx.scene.Node; 042import javafx.scene.input.ContextMenuEvent; 043import javafx.scene.layout.Region; 044import com.sun.javafx.accessible.providers.AccessibleProvider; 045import com.sun.javafx.application.PlatformImpl; 046import com.sun.javafx.css.CssError; 047import javafx.css.CssMetaData; 048import com.sun.javafx.css.StyleManager; 049import javafx.css.StyleableObjectProperty; 050import javafx.css.StyleableStringProperty; 051import com.sun.javafx.css.converters.StringConverter; 052import com.sun.javafx.scene.control.Logging; 053import javafx.css.Styleable; 054import javafx.css.StyleableProperty; 055import sun.util.logging.PlatformLogger; 056 057 058/** 059 * Base class for all user interface controls. A "Control" is a node in the 060 * scene graph which can be manipulated by the user. Controls provide 061 * additional variables and behaviors beyond those of Node to support 062 * common user interactions in a manner which is consistent and predictable 063 * for the user. 064 * <p> 065 * Additionally, controls support explicit skinning to make it easy to 066 * leverage the functionality of a control while customizing its appearance. 067 * <p> 068 * See specific Control subclasses for information on how to use individual 069 * types of controls. 070 * <p> Most controls have their focusTraversable property set to true by default, however 071 * read-only controls such as {@link Label} and {@link ProgressIndicator}, and some 072 * controls that are containers {@link ScrollPane} and {@link ToolBar} do not. 073 * Consult individual control documentation for details. 074 */ 075public abstract class Control extends Region implements Skinnable { 076 077 static { 078 // Ensures that the default application user agent stylesheet is loaded 079 if (Application.getUserAgentStylesheet() == null) { 080 PlatformImpl.setDefaultPlatformUserAgentStylesheet(); 081 } 082 } 083 084 /** 085 * Utility for loading a class in a manner that will work with multiple 086 * class loaders, as is typically found in OSGI modular applications. 087 * In particular, this method will attempt to just load the class 088 * identified by className. If that fails, it attempts to load the 089 * class using the current thread's context class loader. If that fails, 090 * it attempts to use the class loader of the supplied "instance", and 091 * if it still fails it walks up the class hierarchy of the instance 092 * and attempts to use the class loader of each class in the super-type 093 * hierarchy. 094 * 095 * @param className The name of the class we want to load 096 * @param instance An optional instance used to help find the class to load 097 * @return The class. Cannot return null 098 * @throws ClassNotFoundException If the class cannot be found using any technique. 099 */ 100 static Class<?> loadClass(final String className, final Object instance) 101 throws ClassNotFoundException 102 { 103 try { 104 // Try just loading the class 105 return Class.forName(className); 106 } catch (ClassNotFoundException ex) { 107 // RT-17525 : Use context class loader only if Class.forName fails. 108 if (Thread.currentThread().getContextClassLoader() != null) { 109 try { 110 return Thread.currentThread().getContextClassLoader().loadClass(className); 111 } catch (ClassNotFoundException ex2) { 112 // Do nothing, just fall through 113 } 114 } 115 116 // RT-14177: Try looking up the class using the class loader of the 117 // current class, walking up the list of superclasses 118 // and checking each of them, before bailing and using 119 // the context class loader. 120 if (instance != null) { 121 Class<?> currentType = instance.getClass(); 122 while (currentType != null) { 123 try { 124 return currentType.getClassLoader().loadClass(className); 125 } catch (ClassNotFoundException ex2) { 126 currentType = currentType.getSuperclass(); 127 } 128 } 129 } 130 131 // We failed to find the class using any of the above means, so we're going 132 // to just throw the ClassNotFoundException that we caught earlier 133 throw ex; 134 } 135 } 136 137 /*************************************************************************** 138 * * 139 * Private fields * 140 * * 141 **************************************************************************/ 142 143 private List<CssMetaData<? extends Styleable, ?>> styleableProperties; 144 145 /** 146 * A private reference directly to the SkinBase instance that is used as the 147 * Skin for this Control. A Control's Skin doesn't have to be of type 148 * SkinBase, although 98% of the time or greater it probably will be. 149 * Because instanceof checks and reading a value from a property are 150 * not cheap (on interpreters on slower hardware or mobile devices) 151 * it pays to have a direct reference here to the skinBase. We simply 152 * need to check this variable -- if it is not null then we know the 153 * Skin is a SkinBase and this is a direct reference to it. If it is null 154 * then we know the skin is not a SkinBase and we need to call getSkin(). 155 */ 156 private SkinBase<?> skinBase; 157 158 /*************************************************************************** 159 * * 160 * Event Handlers / Listeners * 161 * * 162 **************************************************************************/ 163 164 /** 165 * Handles context menu requests by popping up the menu. 166 * Note that we use this pattern to remove some of the anonymous inner 167 * classes which we'd otherwise have to create. When lambda expressions 168 * are supported, we could do it that way instead (or use MethodHandles). 169 */ 170 private final EventHandler<ContextMenuEvent> contextMenuHandler = new EventHandler<ContextMenuEvent>() { 171 @Override public void handle(ContextMenuEvent event) { 172 // If a context menu was shown, consume the event to prevent multiple context menus 173 if (getContextMenu() != null) { 174 getContextMenu().show(Control.this, event.getScreenX(), event.getScreenY()); 175 event.consume(); 176 } 177 } 178 }; 179 180 181 182 /*************************************************************************** 183 * * 184 * Properties * 185 * * 186 **************************************************************************/ 187 188 189 190 // --- skin 191 /** 192 * Skin is responsible for rendering this {@code Control}. From the 193 * perspective of the {@code Control}, the {@code Skin} is a black box. 194 * It listens and responds to changes in state in a {@code Control}. 195 * <p> 196 * There is a one-to-one relationship between a {@code Control} and its 197 * {@code Skin}. Every {@code Skin} maintains a back reference to the 198 * {@code Control} via the {@link Skin#getSkinnable()} method. 199 * <p> 200 * A skin may be null. 201 */ 202 @Override public final ObjectProperty<Skin<?>> skinProperty() { return skin; } 203 @Override public final void setSkin(Skin<?> value) { 204 skinProperty().set(value); 205 } 206 @Override public final Skin<?> getSkin() { return skinProperty().getValue(); } 207 private ObjectProperty<Skin<?>> skin = new StyleableObjectProperty<Skin<?>>() { 208 // We store a reference to the oldValue so that we can handle 209 // changes in the skin properly in the case of binding. This is 210 // only needed because invalidated() does not currently take 211 // a reference to the old value. 212 private Skin<?> oldValue; 213 214 @Override 215 public void set(Skin<?> v) { 216 if (v == null 217 ? oldValue == null 218 : oldValue != null && v.getClass().equals(oldValue.getClass())) 219 return; 220 221 super.set(v); 222 223 // Collect the name of the currently installed skin class. We do this 224 // so that subsequent updates from CSS to the same skin class will not 225 // result in reinstalling the skin 226 currentSkinClassName = v == null ? null : v.getClass().getName(); 227 228 // If skinClassName is null, then someone called setSkin directly 229 // rather than the skin being set via css. We know this is because 230 // impl_processCSS ensures the skin is set, and impl_processCSS 231 // expands the skin property to see if the skin has been set. 232 // If skinClassName is null, then we need to see if there is 233 // a UA stylesheet at this point since the logic in impl_processCSS 234 // depends on skinClassName being null. 235 if (skinClassName == null) { 236 final String url = Control.this.getUserAgentStylesheet(); 237 if (url != null) { 238 StyleManager.getInstance().addUserAgentStylesheet(url); 239 } 240 } 241 // if someone calls setSkin, we need to make it look like they 242 // called set on skinClassName in order to keep CSS from overwriting 243 // the skin. 244 skinClassNameProperty().set(currentSkinClassName); 245 } 246 247 @Override protected void invalidated() { 248 // Dispose of the old skin 249 if (oldValue != null) oldValue.dispose(); 250 251 // Get the new value, and save it off as the new oldValue 252 final Skin<?> skin = oldValue = getValue(); 253 254 // Reset skinBase to null - it will be set to the new Skin if it 255 // is a SkinBase, otherwise it will remain null, as expected 256 skinBase = null; 257 258 // We have two paths, one for "legacy" Skins, and one for 259 // any Skin which extends from SkinBase. Legacy Skins will 260 // produce a single node which will be the only child of 261 // the Control via the getNode() method on the Skin. A 262 // SkinBase will manipulate the children of the Control 263 // directly. Further, we maintain a direct reference to 264 // the skinBase for more optimal updates later. 265 if (skin instanceof SkinBase) { 266 // record a reference of the skin, if it is a SkinBase, for 267 // performance reasons 268 skinBase = (SkinBase<?>) skin; 269 // Note I do not remove any children here, because the 270 // skin will have already configured all the children 271 // by the time setSkin has been called. This is because 272 // our Skin interface was lacking an initialize method (doh!) 273 // and so the Skin constructor is where it adds listeners 274 // and so forth. For SkinBase implementations, the 275 // constructor is also where it will take ownership of 276 // the children. 277 } else { 278 final Node n = getSkinNode(); 279 if (n != null) { 280 getChildren().setAll(n); 281 } else { 282 getChildren().clear(); 283 } 284 } 285 286 // clear out the styleable properties so that the list is rebuilt 287 // next time they are requested. 288 styleableProperties = null; 289 290 // calling impl_reapplyCSS() as the styleable properties may now 291 // be different, as we will now be able to return styleable properties 292 // belonging to the skin. If impl_reapplyCSS() is not called, the 293 // getCssMetaData() method is never called, so the 294 // skin properties are never exposed. 295 impl_reapplyCSS(); 296 297 // DEBUG: Log that we've changed the skin 298 final PlatformLogger logger = Logging.getControlsLogger(); 299 if (logger.isLoggable(PlatformLogger.FINEST)) { 300 logger.finest("Stored skin[" + getValue() + "] on " + this); 301 } 302 } 303 304 // This method should be CssMetaData<Control,Skin> getCssMetaData(), 305 // but SKIN is CssMetaData<Control,String>. This does not matter to 306 // the CSS code which doesn't care about the actual type. Hence, 307 // we'll suppress the warnings 308 @Override @SuppressWarnings({"unchecked", "rawtype"}) 309 public CssMetaData getCssMetaData() { 310 return StyleableProperties.SKIN; 311 } 312 313 @Override 314 public Object getBean() { 315 return Control.this; 316 } 317 318 @Override 319 public String getName() { 320 return "skin"; 321 } 322 }; 323 324 325 // --- tooltip 326 /** 327 * The ToolTip for this control. 328 */ 329 public final ObjectProperty<Tooltip> tooltipProperty() { 330 if (tooltip == null) { 331 tooltip = new ObjectPropertyBase<Tooltip>() { 332 private Tooltip old = null; 333 @Override protected void invalidated() { 334 Tooltip t = get(); 335 // install / uninstall 336 if (t != old) { 337 if (old != null) { 338 Tooltip.uninstall(Control.this, old); 339 } 340 if (t != null) { 341 Tooltip.install(Control.this, t); 342 } 343 old = t; 344 } 345 } 346 347 @Override 348 public Object getBean() { 349 return Control.this; 350 } 351 352 @Override 353 public String getName() { 354 return "tooltip"; 355 } 356 }; 357 } 358 return tooltip; 359 } 360 private ObjectProperty<Tooltip> tooltip; 361 public final void setTooltip(Tooltip value) { tooltipProperty().setValue(value); } 362 public final Tooltip getTooltip() { return tooltip == null ? null : tooltip.getValue(); } 363 364 365 // --- context menu 366 /** 367 * The ContextMenu to show for this control. 368 */ 369 private ObjectProperty<ContextMenu> contextMenu = new SimpleObjectProperty<ContextMenu>(this, "contextMenu") { 370 @Override protected void invalidated() { 371 // set this flag so contextmenu show will be relative to parent window not anchor 372 ContextMenu ctx = get(); 373 if (ctx != null) ctx.setImpl_showRelativeToWindow(true); //RT-15160 374 } 375 @Override 376 public Object getBean() { 377 return Control.this; 378 } 379 @Override 380 public String getName() { 381 return "contextMenu"; 382 } 383 }; 384 public final ObjectProperty<ContextMenu> contextMenuProperty() { return contextMenu; } 385 public final void setContextMenu(ContextMenu value) { contextMenu.setValue(value); } 386 public final ContextMenu getContextMenu() { return contextMenu == null ? null : contextMenu.getValue(); } 387 388 389 390 /*************************************************************************** 391 * * 392 * Constructors * 393 * * 394 **************************************************************************/ 395 396 /** 397 * Create a new Control. 398 */ 399 protected Control() { 400 // focusTraversable is styleable through css. Calling setFocusTraversable 401 // makes it look to css like the user set the value and css will not 402 // override. Initializing focusTraversable by calling applyStyle 403 // with null for StyleOrigin ensures that css will be able to override 404 // the value. 405 final StyleableProperty<Boolean> prop = (StyleableProperty<Boolean>)focusTraversableProperty(); 406 prop.applyStyle(null, Boolean.TRUE); 407 408 // we add a listener for menu request events to show the context menu 409 // that may be set on the Control 410 this.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler); 411 } 412 413 414 415 /*************************************************************************** 416 * * 417 * Public API * 418 * * 419 **************************************************************************/ 420 421 // Proposed dispose() API. 422 // Note that there is impl code for a dispose method in TableRowSkinBase 423 // and TableCell (just search for dispose()) 424// public void dispose() { 425// Skin skin = getSkin(); 426// if (skin != null) { 427// skin.dispose(); 428// } 429// } 430 431 /** 432 * Returns <code>true</code> since all Controls are resizable. 433 * @return whether this node can be resized by its parent during layout 434 */ 435 @Override public boolean isResizable() { 436 return true; 437 } 438 439 // Implementation of the Resizable interface. 440 // Because only the skin can know the min, pref, and max sizes, these 441 // functions are implemented to delegate to skin. If there is no skin then 442 // we simply return 0 for all the values since a Control without a Skin 443 // doesn't render 444 /** 445 * Computes the minimum allowable width of the Control, based on the provided 446 * height. The minimum width is not calculated within the Control, instead 447 * the calculation is delegated to the {@link Node#minWidth(double)} method 448 * of the {@link Skin}. If the Skin is null, the returned value is 0. 449 * 450 * @param height The height of the Control, in case this value might dictate 451 * the minimum width. 452 * @return A double representing the minimum width of this control. 453 */ 454 @Override protected double computeMinWidth(final double height) { 455 if (skinBase != null) { 456 return skinBase.computeMinWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset()); 457 } else { 458 final Node skinNode = getSkinNode(); 459 return skinNode == null ? 0 : skinNode.minWidth(height); 460 } 461 } 462 463 /** 464 * Computes the minimum allowable height of the Control, based on the provided 465 * width. The minimum height is not calculated within the Control, instead 466 * the calculation is delegated to the {@link Node#minHeight(double)} method 467 * of the {@link Skin}. If the Skin is null, the returned value is 0. 468 * 469 * @param width The width of the Control, in case this value might dictate 470 * the minimum height. 471 * @return A double representing the minimum height of this control. 472 */ 473 @Override protected double computeMinHeight(final double width) { 474 if (skinBase != null) { 475 return skinBase.computeMinHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset()); 476 } else { 477 final Node skinNode = getSkinNode(); 478 return skinNode == null ? 0 : skinNode.minHeight(width); 479 } 480 } 481 482 /** 483 * Computes the maximum allowable width of the Control, based on the provided 484 * height. The maximum width is not calculated within the Control, instead 485 * the calculation is delegated to the {@link Node#maxWidth(double)} method 486 * of the {@link Skin}. If the Skin is null, the returned value is 0. 487 * 488 * @param height The height of the Control, in case this value might dictate 489 * the maximum width. 490 * @return A double representing the maximum width of this control. 491 */ 492 @Override protected double computeMaxWidth(double height) { 493 if (skinBase != null) { 494 return skinBase.computeMaxWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset()); 495 } else { 496 final Node skinNode = getSkinNode(); 497 return skinNode == null ? 0 : skinNode.maxWidth(height); 498 } 499 } 500 501 /** 502 * Computes the maximum allowable height of the Control, based on the provided 503 * width. The maximum height is not calculated within the Control, instead 504 * the calculation is delegated to the {@link Node#maxHeight(double)} method 505 * of the {@link Skin}. If the Skin is null, the returned value is 0. 506 * 507 * @param width The width of the Control, in case this value might dictate 508 * the maximum height. 509 * @return A double representing the maximum height of this control. 510 */ 511 @Override protected double computeMaxHeight(double width) { 512 if (skinBase != null) { 513 return skinBase.computeMaxHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset()); 514 } else { 515 final Node skinNode = getSkinNode(); 516 return skinNode == null ? 0 : skinNode.maxHeight(width); 517 } 518 } 519 520 /** {@inheritDoc} */ 521 @Override protected double computePrefWidth(double height) { 522 if (skinBase != null) { 523 return skinBase.computePrefWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset()); 524 } else { 525 final Node skinNode = getSkinNode(); 526 return skinNode == null ? 0 : skinNode.prefWidth(height); 527 } 528 } 529 530 /** {@inheritDoc} */ 531 @Override protected double computePrefHeight(double width) { 532 if (skinBase != null) { 533 return skinBase.computePrefHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset()); 534 } else { 535 final Node skinNode = getSkinNode(); 536 return skinNode == null ? 0 : skinNode.prefHeight(width); 537 } 538 } 539 540 /** {@inheritDoc} */ 541 @Override public double getBaselineOffset() { 542 if (skinBase != null) { 543 return skinBase.computeBaselineOffset(snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset()); 544 } else { 545 final Node skinNode = getSkinNode(); 546 return skinNode == null ? 0 : skinNode.getBaselineOffset(); 547 } 548 } 549 550 /*************************************************************************** 551 * Implementation of layout bounds for the Control. We want to preserve * 552 * the lazy semantics of layout bounds. So whenever the width/height * 553 * changes on the node, we end up invalidating layout bounds. We then * 554 * recompute it on demand. * 555 **************************************************************************/ 556 557 /** {@inheritDoc} */ 558 @Override protected void layoutChildren() { 559 if (skinBase != null) { 560 final double x = snappedLeftInset(); 561 final double y = snappedTopInset(); 562 final double w = snapSize(getWidth()) - x - snappedRightInset(); 563 final double h = snapSize(getHeight()) - y - snappedBottomInset(); 564 skinBase.layoutChildren(x, y, w, h); 565 } else { 566 Node n = getSkinNode(); 567 if (n != null) { 568 n.resizeRelocate(0, 0, getWidth(), getHeight()); 569 } 570 } 571 } 572 573 /*************************************************************************** 574 * Forward the following to the skin * 575 **************************************************************************/ 576 577 /** 578 * Create a new instance of the default skin for this control. This is called to create a skin for the control if 579 * no skin is provided via CSS {@code -fx-skin} or set explicitly in a sub-class with {@code setSkin(...)}. 580 * 581 * @return new instance of default skin for this control. If null then the control will have no skin unless one 582 * is provided by css. 583 */ 584 protected Skin<?> createDefaultSkin() { 585 return null; 586 } 587 588 /*************************************************************************** 589 * * 590 * Package API for SkinBase * 591 * * 592 **************************************************************************/ 593 594 // package private for SkinBase 595 ObservableList<Node> getControlChildren() { 596 return getChildren(); 597 } 598 599 600 /*************************************************************************** 601 * * 602 * Private implementation * 603 * * 604 **************************************************************************/ 605 606 /** 607 * Gets the Skin's node, or returns null if there is no Skin. 608 * Convenience method for getting the node of the skin. This is null-safe, 609 * meaning if skin is null then it will return null instead of throwing 610 * a NullPointerException. 611 * 612 * @return The Skin's node, or null. 613 */ 614 private Node getSkinNode() { 615 assert skinBase == null; 616 Skin<?> skin = getSkin(); 617 return skin == null ? null : skin.getNode(); 618 } 619 620 /** 621 * Keeps a reference to the name of the class currently acting as the skin. 622 */ 623 private String currentSkinClassName = null; 624 private StringProperty skinClassName; 625 626 /** 627 * @treatAsPrivate 628 */ 629 @Deprecated protected StringProperty skinClassNameProperty() { 630 if (skinClassName == null) { 631 skinClassName = new StyleableStringProperty() { 632 633 @Override 634 public void set(String v) { 635 // do not allow the skin to be set to null through CSS 636 if (v == null || v.isEmpty() || v.equals(get())) return; 637 super.set(v); 638 } 639 640 @Override 641 public void invalidated() { 642 // reset the styleable properties so we get the new ones from 643 // the new skin 644 styleableProperties = null; 645 646 if (get() != null) { 647 if (!get().equals(currentSkinClassName)) { 648 loadSkinClass(); 649 } 650 // Note: CSS should not set skin to null 651 } 652 } 653 654 @Override 655 public Object getBean() { 656 return Control.this; 657 } 658 659 @Override 660 public String getName() { 661 return "skinClassName"; 662 } 663 664 @Override 665 public CssMetaData<Control,String> getCssMetaData() { 666 return StyleableProperties.SKIN; 667 } 668 669 }; 670 } 671 return skinClassName; 672 } 673 674 private void loadSkinClass() { 675 if (skinClassName == null 676 || skinClassName.get() == null 677 || skinClassName.get().isEmpty()) { 678 final String msg = 679 "Empty -fx-skin property specified for control " + this; 680 final List<CssError> errors = StyleManager.getErrors(); 681 if (errors != null) { 682 CssError error = new CssError(msg); 683 errors.add(error); // RT-19884 684 } 685 Logging.getControlsLogger().severe(msg); 686 return; 687 } 688 689 try { 690 final Class<?> skinClass = Control.loadClass(skinClassName.get(), this); 691 Constructor<?>[] constructors = skinClass.getConstructors(); 692 Constructor<?> skinConstructor = null; 693 for (Constructor<?> c : constructors) { 694 Class<?>[] parameterTypes = c.getParameterTypes(); 695 if (parameterTypes.length == 1 && Control.class.isAssignableFrom(parameterTypes[0])) { 696 skinConstructor = c; 697 break; 698 } 699 } 700 701 if (skinConstructor == null) { 702 final String msg = 703 "No valid constructor defined in '" + skinClassName + "' for control " + this + 704 ".\r\nYou must provide a constructor that accepts a single " 705 + "Control parameter in " + skinClassName + "."; 706 final List<CssError> errors = StyleManager.getErrors(); 707 if (errors != null) { 708 CssError error = new CssError(msg); 709 errors.add(error); // RT-19884 710 } 711 Logging.getControlsLogger().severe(msg); 712 } else { 713 Skin<?> skinInstance = (Skin<?>) skinConstructor.newInstance(this); 714 // Do not call setSkin here since it has the side effect of 715 // also setting the skinClassName! 716 skinProperty().set(skinInstance); 717 } 718 } catch (InvocationTargetException e) { 719 final String msg = 720 "Failed to load skin '" + skinClassName + "' for control " + this; 721 final List<CssError> errors = StyleManager.getErrors(); 722 if (errors != null) { 723 CssError error = new CssError(msg + " :" + e.getLocalizedMessage()); 724 errors.add(error); // RT-19884 725 } 726 Logging.getControlsLogger().severe(msg, e.getCause()); 727 } catch (Exception e) { 728 final String msg = 729 "Failed to load skin '" + skinClassName + "' for control " + this; 730 final List<CssError> errors = StyleManager.getErrors(); 731 if (errors != null) { 732 CssError error = new CssError(msg + " :" + e.getLocalizedMessage()); 733 errors.add(error); // RT-19884 734 } 735 Logging.getControlsLogger().severe(msg, e); 736 } 737 } 738 739 /*************************************************************************** 740 * * 741 * StyleSheet Handling * 742 * * 743 **************************************************************************/ 744 745 /** 746 * Implementors may specify their own user-agent stylesheet. The return 747 * value is a string URL. A relative URL is resolved using the class 748 * loader of the implementing Control. 749 * @return A string URL 750 */ 751 protected String getUserAgentStylesheet() { 752 return null; 753 } 754 755 private static class StyleableProperties { 756 private static final CssMetaData<Control,String> SKIN = 757 new CssMetaData<Control,String>("-fx-skin", 758 StringConverter.getInstance()) { 759 760 @Override 761 public boolean isSettable(Control n) { 762 return (n.skin == null || !n.skin.isBound()); 763 } 764 765 @Override 766 public StyleableProperty<String> getStyleableProperty(Control n) { 767 return (StyleableProperty<String>)n.skinClassNameProperty(); 768 } 769 }; 770 771 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 772 static { 773 final List<CssMetaData<? extends Styleable, ?>> styleables = 774 new ArrayList<CssMetaData<? extends Styleable, ?>>(Region.getClassCssMetaData()); 775 styleables.add(SKIN); 776 STYLEABLES = Collections.unmodifiableList(styleables); 777 } 778 } 779 780 /** 781 * @return The CssMetaData associated with this class, which may include the 782 * CssMetaData of its super classes. 783 */ 784 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 785 return StyleableProperties.STYLEABLES; 786 } 787 788 /** 789 * This method returns a {@link List} containing all {@link CssMetaData} for 790 * both this Control (returned from {@link #getControlCssMetaData()} and its 791 * {@link Skin}, assuming the {@link #skinProperty() skin property} is a 792 * {@link SkinBase}. 793 * 794 * <p>Developers who wish to provide custom CssMetaData are therefore 795 * encouraged to override {@link Control#getControlCssMetaData()} or 796 * {@link SkinBase#getCssMetaData()}, depending on where the CssMetaData 797 * resides. 798 */ 799 @Override 800 public final List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 801 if (styleableProperties == null) { 802 803 // RT-29162: make sure properties only show up once in the list 804 java.util.Map<String, CssMetaData<? extends Styleable, ?>> map = 805 new java.util.HashMap<String, CssMetaData<? extends Styleable, ?>>(); 806 807 List<CssMetaData<? extends Styleable, ?>> list = getControlCssMetaData(); 808 809 for (int n=0, nMax = list != null ? list.size() : 0; n<nMax; n++) { 810 811 CssMetaData<? extends Styleable, ?> metaData = list.get(n); 812 if (metaData == null) continue; 813 814 map.put(metaData.getProperty(), metaData); 815 } 816 817 // 818 // if both control and skin base have the same property, use the 819 // one from skin base since it may be a specialization of the 820 // property in the control. For instance, Label has -fx-font and 821 // so does LabeledText which is Label's skin. 822 // 823 list = skinBase != null ? skinBase.getCssMetaData() : null; 824 825 for (int n=0, nMax = list != null ? list.size() : 0; n<nMax; n++) { 826 827 CssMetaData<? extends Styleable, ?> metaData = list.get(n); 828 if (metaData == null) continue; 829 830 map.put(metaData.getProperty(), metaData); 831 } 832 833 styleableProperties = new ArrayList<CssMetaData<? extends Styleable, ?>>(); 834 styleableProperties.addAll(map.values()); 835 } 836 return styleableProperties; 837 } 838 839 /** 840 * @return unmodifiable list of the controls css styleable properties 841 */ 842 protected List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() { 843 return getClassCssMetaData(); 844 } 845 846 /** 847 * @treatAsPrivate implementation detail 848 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 849 */ 850 @Deprecated 851 @Override protected void impl_processCSS() { 852 if (skinClassNameProperty().get() == null) { 853 final String url = Control.this.getUserAgentStylesheet(); 854 if (url != null) { 855 StyleManager.getInstance().addUserAgentStylesheet(url); 856 } 857 } 858 859 super.impl_processCSS(); 860 861 if (getSkin() == null) { 862 // try to create default skin 863 final Skin<?> defaultSkin = createDefaultSkin(); 864 if (defaultSkin != null) { 865 skinProperty().set(defaultSkin); 866 // we have to reapply css again so that the newly set skin gets css applied as well. 867 super.impl_processCSS(); 868 } else { 869 final String msg = "The -fx-skin property has not been defined in CSS for " + this + 870 " and createDefaultSkin() returned null."; 871 final List<CssError> errors = StyleManager.getErrors(); 872 if (errors != null) { 873 CssError error = new CssError(msg); 874 errors.add(error); // RT-19884 875 } 876 Logging.getControlsLogger().severe(msg); 877 } 878 } 879 } 880 881 /** 882 * Most Controls return true for focusTraversable initial value. 883 * This method is called from CSS code to get the correct initial value. 884 * @treatAsPrivate implementation detail 885 */ 886 @Deprecated @Override 887 protected /*do not make final*/ Boolean impl_cssGetFocusTraversableInitialValue() { 888 return Boolean.TRUE; 889 } 890 891 /** 892 * @treatAsPrivate implementation detail 893 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 894 */ 895 @Deprecated public AccessibleProvider impl_getAccessible() { 896 return null; // return a valid value for specific controls accessible objects 897 } 898 899}