1 /*
   2  * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control;
  27 
  28 import com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler;
  29 import javafx.beans.value.ObservableValue;
  30 import javafx.css.CssMetaData;
  31 import javafx.css.PseudoClass;
  32 import java.util.Collections;
  33 import java.util.List;
  34 import java.util.function.Consumer;
  35 
  36 import javafx.collections.ObservableList;
  37 import javafx.css.Styleable;
  38 import javafx.event.EventHandler;
  39 import javafx.geometry.HPos;
  40 import javafx.geometry.Insets;
  41 import javafx.geometry.VPos;
  42 import javafx.scene.AccessibleAction;
  43 import javafx.scene.AccessibleAttribute;
  44 import javafx.scene.Node;
  45 import javafx.scene.input.MouseEvent;
  46 import javafx.scene.layout.Region;
  47 
  48 /**
  49  * Base implementation class for defining the visual representation of user
  50  * interface controls by defining a scene graph of nodes to represent the
  51  * {@link Skin skin}.
  52  * A user interface control is abstracted behind the {@link Skinnable} interface.
  53  *
  54  * @since JavaFX 8.0
  55  */
  56 public abstract class SkinBase<C extends Control> implements Skin<C> {
  57 
  58     /***************************************************************************
  59      *                                                                         *
  60      * Private fields                                                          *
  61      *                                                                         *
  62      **************************************************************************/
  63 
  64     /**
  65      * The {@code Control} that is referencing this Skin. There is a
  66      * one-to-one relationship between a {@code Skin} and a {@code Control}.
  67      * When a {@code Skin} is set on a {@code Control}, this variable is
  68      * automatically updated.
  69      */
  70     private C control;
  71 
  72     /**
  73      * A local field that directly refers to the children list inside the Control.
  74      */
  75     private ObservableList<Node> children;
  76 
  77     /**
  78      * This is part of the workaround introduced during delomboking. We probably will
  79      * want to adjust the way listeners are added rather than continuing to use this
  80      * map (although it doesn't really do much harm).
  81      */
  82     private LambdaMultiplePropertyChangeListenerHandler lambdaChangeListenerHandler;
  83 
  84 
  85 
  86     /***************************************************************************
  87      *                                                                         *
  88      * Event Handlers / Listeners                                              *
  89      *                                                                         *
  90      **************************************************************************/
  91 
  92     /**
  93      * Mouse handler used for consuming all mouse events (preventing them
  94      * from bubbling up to parent)
  95      */
  96     private static final EventHandler<MouseEvent> mouseEventConsumer = event -> {
  97         /*
  98         ** we used to consume mouse wheel rotations here,
  99         ** be we've switched to ScrollEvents, and only consume those which we use.
 100         ** See RT-13995 & RT-14480
 101         */
 102         event.consume();
 103     };
 104 
 105 
 106 
 107     /***************************************************************************
 108      *                                                                         *
 109      * Constructor                                                             *
 110      *                                                                         *
 111      **************************************************************************/
 112 
 113     /**
 114      * Constructor for all SkinBase instances.
 115      *
 116      * @param control The control for which this Skin should attach to.
 117      */
 118     protected SkinBase(final C control) {
 119         if (control == null) {
 120             throw new IllegalArgumentException("Cannot pass null for control");
 121         }
 122 
 123         // Update the control and behavior
 124         this.control = control;
 125         this.children = control.getControlChildren();
 126 
 127         // Default behavior for controls is to consume all mouse events
 128         consumeMouseEvents(true);
 129     }
 130 
 131 
 132 
 133     /***************************************************************************
 134      *                                                                         *
 135      * Public API (from Skin)                                                  *
 136      *                                                                         *
 137      **************************************************************************/
 138 
 139     /** {@inheritDoc} */
 140     @Override public final C getSkinnable() {
 141         return control;
 142     }
 143 
 144     /** {@inheritDoc} */
 145     @Override public final Node getNode() {
 146         return control;
 147     }
 148 
 149     /** {@inheritDoc} */
 150     @Override public void dispose() {
 151 //        control.removeEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler);
 152 
 153         // unhook listeners
 154         if (lambdaChangeListenerHandler != null) {
 155             lambdaChangeListenerHandler.dispose();
 156         }
 157 
 158         this.control = null;
 159     }
 160 
 161 
 162 
 163     /***************************************************************************
 164      *                                                                         *
 165      * Public API                                                              *
 166      *                                                                         *
 167      **************************************************************************/
 168 
 169     /**
 170      * Returns the children of the skin.
 171      * @return the children of the skin
 172      */
 173     public final ObservableList<Node> getChildren() {
 174         return children;
 175     }
 176 
 177     /**
 178      * Called during the layout pass of the scenegraph.
 179      * @param contentX the x position
 180      * @param contentY the y position
 181      * @param contentWidth the width
 182      * @param contentHeight the height
 183      */
 184     protected void layoutChildren(final double contentX, final double contentY,
 185             final double contentWidth, final double contentHeight) {
 186         // By default simply sizes all managed children to fit within the space provided
 187         for (int i=0, max=children.size(); i<max; i++) {
 188             Node child = children.get(i);
 189             if (child.isManaged()) {
 190                 layoutInArea(child, contentX, contentY, contentWidth, contentHeight, -1, HPos.CENTER, VPos.CENTER);
 191             }
 192         }
 193     }
 194 
 195     /**
 196      * Determines whether all mouse events should be automatically consumed.
 197      * @param value the consume mouse events flag
 198      */
 199     protected final void consumeMouseEvents(boolean value) {
 200         if (value) {
 201             control.addEventHandler(MouseEvent.ANY, mouseEventConsumer);
 202         } else {
 203             control.removeEventHandler(MouseEvent.ANY, mouseEventConsumer);
 204         }
 205     }
 206 
 207 
 208     /**
 209      * Subclasses can invoke this method to register that they want to listen to
 210      * property change events for the given property. Registered {@link Consumer} instances
 211      * will be executed in the order in which they are registered.
 212      * @param property the property
 213      * @param consumer the consumer
 214      */
 215     protected final void registerChangeListener(ObservableValue<?> property, Consumer<ObservableValue<?>> consumer) {
 216         if (lambdaChangeListenerHandler == null) {
 217             lambdaChangeListenerHandler = new LambdaMultiplePropertyChangeListenerHandler();
 218         }
 219         lambdaChangeListenerHandler.registerChangeListener(property, consumer);
 220     }
 221 
 222     /**
 223      * Unregisters all change listeners that have been registered using {@link #registerChangeListener(ObservableValue, Consumer)}
 224      * for the given property. The end result is that the given property is no longer observed by any of the change
 225      * listeners, but it may still have additional listeners registered on it through means outside of
 226      * {@link #registerChangeListener(ObservableValue, Consumer)}.
 227      *
 228      * @param property The property for which all listeners should be removed.
 229      * @return A single chained {@link Consumer} consisting of all {@link Consumer consumers} registered through
 230      *      {@link #registerChangeListener(ObservableValue, Consumer)}. If no consumers have been registered on this
 231      *      property, null will be returned.
 232      * @since 9
 233      */
 234     protected final Consumer<ObservableValue<?>> unregisterChangeListeners(ObservableValue<?> property) {
 235         if (lambdaChangeListenerHandler == null) {
 236             return null;
 237         }
 238         return lambdaChangeListenerHandler.unregisterChangeListeners(property);
 239     }
 240 
 241 
 242 
 243 
 244     /***************************************************************************
 245      *                                                                         *
 246      * Public Layout-related API                                               *
 247      *                                                                         *
 248      **************************************************************************/
 249 
 250     /**
 251      * Computes the minimum allowable width of the Skin, based on the provided
 252      * height.
 253      *
 254      * @param height The height of the Skin, in case this value might dictate
 255      *      the minimum width.
 256      * @param topInset the pixel snapped top inset
 257      * @param rightInset the pixel snapped right inset
 258      * @param bottomInset the pixel snapped bottom inset
 259      * @param leftInset  the pixel snapped left inset
 260      * @return A double representing the minimum width of this Skin.
 261      */
 262     protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 263 
 264         double minX = 0;
 265         double maxX = 0;
 266         boolean firstManagedChild = true;
 267         for (int i = 0; i < children.size(); i++) {
 268             Node node = children.get(i);
 269             if (node.isManaged()) {
 270                 final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
 271                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 272                     minX = Math.min(minX, x);
 273                     maxX = Math.max(maxX, x + node.minWidth(-1));
 274                 } else {
 275                     minX = x;
 276                     maxX = x + node.minWidth(-1);
 277                     firstManagedChild = false;
 278                 }
 279             }
 280         }
 281         double minWidth = maxX - minX;
 282         return leftInset + minWidth + rightInset;
 283     }
 284 
 285     /**
 286      * Computes the minimum allowable height of the Skin, based on the provided
 287      * width.
 288      *
 289      * @param width The width of the Skin, in case this value might dictate
 290      *      the minimum height.
 291      * @param topInset the pixel snapped top inset
 292      * @param rightInset the pixel snapped right inset
 293      * @param bottomInset the pixel snapped bottom inset
 294      * @param leftInset  the pixel snapped left inset
 295      * @return A double representing the minimum height of this Skin.
 296      */
 297     protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 298 
 299         double minY = 0;
 300         double maxY = 0;
 301         boolean firstManagedChild = true;
 302         for (int i = 0; i < children.size(); i++) {
 303             Node node = children.get(i);
 304             if (node.isManaged()) {
 305                 final double y = node.getLayoutBounds().getMinY() + node.getLayoutY();
 306                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 307                     minY = Math.min(minY, y);
 308                     maxY = Math.max(maxY, y + node.minHeight(-1));
 309                 } else {
 310                     minY = y;
 311                     maxY = y + node.minHeight(-1);
 312                     firstManagedChild = false;
 313                 }
 314             }
 315         }
 316         double minHeight = maxY - minY;
 317         return topInset + minHeight + bottomInset;
 318     }
 319 
 320     /**
 321      * Computes the maximum allowable width of the Skin, based on the provided
 322      * height.
 323      *
 324      * @param height The height of the Skin, in case this value might dictate
 325      *      the maximum width.
 326      * @param topInset the pixel snapped top inset
 327      * @param rightInset the pixel snapped right inset
 328      * @param bottomInset the pixel snapped bottom inset
 329      * @param leftInset  the pixel snapped left inset
 330      * @return A double representing the maximum width of this Skin.
 331      */
 332     protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 333         return Double.MAX_VALUE;
 334     }
 335 
 336     /**
 337      * Computes the maximum allowable height of the Skin, based on the provided
 338      * width.
 339      *
 340      * @param width The width of the Skin, in case this value might dictate
 341      *      the maximum height.
 342      * @param topInset the pixel snapped top inset
 343      * @param rightInset the pixel snapped right inset
 344      * @param bottomInset the pixel snapped bottom inset
 345      * @param leftInset  the pixel snapped left inset
 346      * @return A double representing the maximum height of this Skin.
 347      */
 348     protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 349         return Double.MAX_VALUE;
 350     }
 351 
 352     // PENDING_DOC_REVIEW
 353     /**
 354      * Calculates the preferred width of this {@code SkinBase}. The default
 355      * implementation calculates this width as the width of the area occupied
 356      * by its managed children when they are positioned at their
 357      * current positions at their preferred widths.
 358      *
 359      * @param height the height that should be used if preferred width depends on it
 360      * @param topInset the pixel snapped top inset
 361      * @param rightInset the pixel snapped right inset
 362      * @param bottomInset the pixel snapped bottom inset
 363      * @param leftInset  the pixel snapped left inset
 364      * @return the calculated preferred width
 365      */
 366     protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 367 
 368         double minX = 0;
 369         double maxX = 0;
 370         boolean firstManagedChild = true;
 371         for (int i = 0; i < children.size(); i++) {
 372             Node node = children.get(i);
 373             if (node.isManaged()) {
 374                 final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
 375                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 376                     minX = Math.min(minX, x);
 377                     maxX = Math.max(maxX, x + node.prefWidth(-1));
 378                 } else {
 379                     minX = x;
 380                     maxX = x + node.prefWidth(-1);
 381                     firstManagedChild = false;
 382                 }
 383             }
 384         }
 385         return maxX - minX;
 386     }
 387 
 388     // PENDING_DOC_REVIEW
 389     /**
 390      * Calculates the preferred height of this {@code SkinBase}. The default
 391      * implementation calculates this height as the height of the area occupied
 392      * by its managed children when they are positioned at their current
 393      * positions at their preferred heights.
 394      *
 395      * @param width the width that should be used if preferred height depends on it
 396      * @param topInset the pixel snapped top inset
 397      * @param rightInset the pixel snapped right inset
 398      * @param bottomInset the pixel snapped bottom inset
 399      * @param leftInset  the pixel snapped left inset
 400      * @return the calculated preferred height
 401      */
 402     protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 403 
 404         double minY = 0;
 405         double maxY = 0;
 406         boolean firstManagedChild = true;
 407         for (int i = 0; i < children.size(); i++) {
 408             Node node = children.get(i);
 409             if (node.isManaged()) {
 410                 final double y = node.getLayoutBounds().getMinY() + node.getLayoutY();
 411                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 412                     minY = Math.min(minY, y);
 413                     maxY = Math.max(maxY, y + node.prefHeight(-1));
 414                 } else {
 415                     minY = y;
 416                     maxY = y + node.prefHeight(-1);
 417                     firstManagedChild = false;
 418                 }
 419             }
 420         }
 421         return maxY - minY;
 422     }
 423 
 424     /**
 425      * Calculates the baseline offset based on the first managed child. If there
 426      * is no such child, returns {@link Node#getBaselineOffset()}.
 427      *
 428      * @param topInset the pixel snapped top inset
 429      * @param rightInset the pixel snapped right inset
 430      * @param bottomInset the pixel snapped bottom inset
 431      * @param leftInset  the pixel snapped left inset
 432      * @return baseline offset
 433      */
 434     protected double computeBaselineOffset(double topInset, double rightInset, double bottomInset, double leftInset) {
 435         int size = children.size();
 436         for (int i = 0; i < size; ++i) {
 437             Node child = children.get(i);
 438             if (child.isManaged()) {
 439                 double offset = child.getBaselineOffset();
 440                 if (offset == Node.BASELINE_OFFSET_SAME_AS_HEIGHT) {
 441                     continue;
 442                 }
 443                 return child.getLayoutBounds().getMinY() + child.getLayoutY() + offset;
 444             }
 445         }
 446         return Node.BASELINE_OFFSET_SAME_AS_HEIGHT;
 447     }
 448 
 449 
 450     /***************************************************************************
 451      *                                                                         *
 452      * (Mostly ugly) Skin -> Control forwarding API                            *
 453      *                                                                         *
 454      **************************************************************************/
 455 
 456     /**
 457      * Utility method to get the top inset which includes padding and border
 458      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 459      *
 460      * @return Rounded up insets top
 461      */
 462     protected double snappedTopInset() {
 463         return control.snappedTopInset();
 464     }
 465 
 466     /**
 467      * Utility method to get the bottom inset which includes padding and border
 468      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 469      *
 470      * @return Rounded up insets bottom
 471      */
 472     protected double snappedBottomInset() {
 473         return control.snappedBottomInset();
 474     }
 475 
 476     /**
 477      * Utility method to get the left inset which includes padding and border
 478      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 479      *
 480      * @return Rounded up insets left
 481      */
 482     protected double snappedLeftInset() {
 483         return control.snappedLeftInset();
 484     }
 485 
 486     /**
 487      * Utility method to get the right inset which includes padding and border
 488      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 489      *
 490      * @return Rounded up insets right
 491      */
 492     protected double snappedRightInset() {
 493         return control.snappedRightInset();
 494     }
 495 
 496     /**
 497      * If {@code getSkinnable().isSnapToPixel()} is false, this method
 498      * returns the same value, else it tries to return a value rounded to
 499      * the nearest pixel, but since there is no indication if the value is
 500      * a vertical or horizontal measurement then it may be snapped to the
 501      * wrong pixel size metric on screens with different horizontal and
 502      * vertical scales.
 503      * @param value the space value to be snapped
 504      * @return value rounded to nearest pixel
 505      * @deprecated replaced by {@code snapSpaceX()} and {@code snapSpaceY()}
 506      */
 507     @Deprecated(since="9")
 508     protected double snapSpace(double value) {
 509         return control.snapSpaceX(value);
 510     }
 511 
 512     /**
 513      * Convenience method for accessing the
 514      * {@link Region#snapSpaceX(double) snapSpaceX()}
 515      * method on the skinnable.
 516      * It is equivalent to calling
 517      * {@code getSkinnable().snapSpaceX(value)}.
 518      * @param value the space value to be snapped
 519      * @return value rounded to nearest pixel
 520      * @see Region#snapSpaceX(double)
 521      * @since 9
 522      */
 523     protected double snapSpaceX(double value) {
 524         return control.snapSpaceX(value);
 525     }
 526 
 527     /**
 528      * Convenience method for accessing the
 529      * {@link Region#snapSpaceY(double) snapSpaceY()}
 530      * method on the skinnable.
 531      * It is equivalent to calling
 532      * {@code getSkinnable().snapSpaceY(value)}.
 533      * @param value the space value to be snapped
 534      * @return value rounded to nearest pixel
 535      * @see Region#snapSpaceY(double)
 536      * @since 9
 537      */
 538     protected double snapSpaceY(double value) {
 539         return control.snapSpaceY(value);
 540     }
 541 
 542     /**
 543      * If {@code getSkinnable().isSnapToPixel()} is false, this method
 544      * returns the same value, else it tries to return a value ceiled to
 545      * the nearest pixel, but since there is no indication if the value is
 546      * a vertical or horizontal measurement then it may be snapped to the
 547      * wrong pixel size metric on screens with different horizontal and
 548      * vertical scales.
 549      * @param value the size value to be snapped
 550      * @return value ceiled to nearest pixel
 551      * @deprecated replaced by {@code snapSizeX()} and {@code snapSizeY()}
 552      */
 553     @Deprecated(since="9")
 554     protected double snapSize(double value) {
 555         return control.snapSizeX(value);
 556     }
 557 
 558     /**
 559      * Convenience method for accessing the
 560      * {@link Region#snapSizeX(double) snapSizeX()}
 561      * method on the skinnable.
 562      * It is equivalent to calling
 563      * {@code getSkinnable().snapSizeX(value)}.
 564      * @param value the size value to be snapped
 565      * @return value ceiled to nearest pixel
 566      * @see Region#snapSizeX(double)
 567      * @since 9
 568      */
 569     protected double snapSizeX(double value) {
 570         return control.snapSizeX(value);
 571     }
 572 
 573     /**
 574      * Convenience method for accessing the
 575      * {@link Region#snapSizeY(double) snapSizeY()}
 576      * method on the skinnable.
 577      * It is equivalent to calling
 578      * {@code getSkinnable().snapSizeY(value)}.
 579      * @param value the size value to be snapped
 580      * @return value ceiled to nearest pixel
 581      * @see Region#snapSizeY(double)
 582      * @since 9
 583      */
 584     protected double snapSizeY(double value) {
 585         return control.snapSizeY(value);
 586     }
 587 
 588     /**
 589      * If {@code getSkinnable().isSnapToPixel()} is false, this method
 590      * returns the same value, else it tries to return a value rounded to
 591      * the nearest pixel, but since there is no indication if the value is
 592      * a vertical or horizontal measurement then it may be snapped to the
 593      * wrong pixel size metric on screens with different horizontal and
 594      * vertical scales.
 595      * @param value the position value to be snapped
 596      * @return value rounded to nearest pixel
 597      * @deprecated replaced by {@code snapPositionX()} and {@code snapPositionY()}
 598      */
 599     @Deprecated(since="9")
 600     protected double snapPosition(double value) {
 601         return control.snapPositionX(value);
 602     }
 603 
 604     /**
 605      * Convenience method for accessing the
 606      * {@link Region#snapPositionX(double) snapPositionX()}
 607      * method on the skinnable.
 608      * It is equivalent to calling
 609      * {@code getSkinnable().snapPositionX(value)}.
 610      * @param value the position value to be snapped
 611      * @return value rounded to nearest pixel
 612      * @see Region#snapPositionX(double)
 613      * @since 9
 614      */
 615     protected double snapPositionX(double value) {
 616         return control.snapPositionX(value);
 617     }
 618 
 619     /**
 620      * Convenience method for accessing the
 621      * {@link Region#snapPositionY(double) snapPositionY()}
 622      * method on the skinnable.
 623      * It is equivalent to calling
 624      * {@code getSkinnable().snapPositionY(value)}.
 625      * @param value the position value to be snapped
 626      * @return value rounded to nearest pixel
 627      * @see Region#snapPositionY(double)
 628      * @since 9
 629      */
 630     protected double snapPositionY(double value) {
 631         return control.snapPositionY(value);
 632     }
 633 
 634     /**
 635      * Utility method which positions the child within an area of this
 636      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 637      * with a baseline offset relative to that area.
 638      * <p>
 639      * This function does <i>not</i> resize the node and uses the node's layout bounds
 640      * width and height to determine how it should be positioned within the area.
 641      * <p>
 642      * If the vertical alignment is {@code VPos.BASELINE} then it
 643      * will position the node so that its own baseline aligns with the passed in
 644      * {@code baselineOffset}, otherwise the baseline parameter is ignored.
 645      * <p>
 646      * If {@code snapToPixel} is {@code true} for this skin, then the x/y position
 647      * values will be rounded to their nearest pixel boundaries.
 648      *
 649      * @param child the child being positioned within this skin
 650      * @param areaX the horizontal offset of the layout area relative to this skin
 651      * @param areaY the vertical offset of the layout area relative to this skin
 652      * @param areaWidth  the width of the layout area
 653      * @param areaHeight the height of the layout area
 654      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 655      * @param halignment the horizontal alignment for the child within the area
 656      * @param valignment the vertical alignment for the child within the area
 657      *
 658      */
 659     protected void positionInArea(Node child, double areaX, double areaY,
 660             double areaWidth, double areaHeight, double areaBaselineOffset,
 661             HPos halignment, VPos valignment) {
 662         positionInArea(child, areaX, areaY, areaWidth, areaHeight,
 663                 areaBaselineOffset, Insets.EMPTY, halignment, valignment);
 664     }
 665 
 666     /**
 667      * Utility method which positions the child within an area of this
 668      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 669      * with a baseline offset relative to that area.
 670      * <p>
 671      * This function does <i>not</i> resize the node and uses the node's layout bounds
 672      * width and height to determine how it should be positioned within the area.
 673      * <p>
 674      * If the vertical alignment is {@code VPos.BASELINE} then it
 675      * will position the node so that its own baseline aligns with the passed in
 676      * {@code baselineOffset},  otherwise the baseline parameter is ignored.
 677      * <p>
 678      * If {@code snapToPixel} is {@code true} for this skin, then the x/y position
 679      * values will be rounded to their nearest pixel boundaries.
 680      * <p>
 681      * If {@code margin} is non-null, then that space will be allocated around the
 682      * child within the layout area.  margin may be null.
 683      *
 684      * @param child the child being positioned within this skin
 685      * @param areaX the horizontal offset of the layout area relative to this skin
 686      * @param areaY the vertical offset of the layout area relative to this skin
 687      * @param areaWidth  the width of the layout area
 688      * @param areaHeight the height of the layout area
 689      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 690      * @param margin the margin of space to be allocated around the child
 691      * @param halignment the horizontal alignment for the child within the area
 692      * @param valignment the vertical alignment for the child within the area
 693      *
 694      * @since JavaFX 8.0
 695      */
 696     protected void positionInArea(Node child, double areaX, double areaY,
 697             double areaWidth, double areaHeight, double areaBaselineOffset,
 698             Insets margin, HPos halignment, VPos valignment) {
 699         Region.positionInArea(child, areaX, areaY, areaWidth, areaHeight,
 700                 areaBaselineOffset, margin, halignment, valignment,
 701                 control.isSnapToPixel());
 702     }
 703 
 704     /**
 705      * Utility method which lays out the child within an area of this
 706      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 707      * with a baseline offset relative to that area.
 708      * <p>
 709      * If the child is resizable, this method will resize it to fill the specified
 710      * area unless the node's maximum size prevents it.  If the node's maximum
 711      * size preference is less than the area size, the maximum size will be used.
 712      * If node's maximum is greater than the area size, then the node will be
 713      * resized to fit within the area, unless its minimum size prevents it.
 714      * <p>
 715      * If the child has a non-null contentBias, then this method will use it when
 716      * resizing the child.  If the contentBias is horizontal, it will set its width
 717      * first to the area's width (up to the child's max width limit) and then pass
 718      * that value to compute the child's height.  If child's contentBias is vertical,
 719      * then it will set its height to the area height (up to child's max height limit)
 720      * and pass that height to compute the child's width.  If the child's contentBias
 721      * is null, then it's width and height have no dependencies on each other.
 722      * <p>
 723      * If the child is not resizable (Shape, Group, etc) then it will only be
 724      * positioned and not resized.
 725      * <p>
 726      * If the child's resulting size differs from the area's size (either
 727      * because it was not resizable or it's sizing preferences prevented it), then
 728      * this function will align the node relative to the area using horizontal and
 729      * vertical alignment values.
 730      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 731      * with the area baseline offset parameter, otherwise the baseline parameter
 732      * is ignored.
 733      * <p>
 734      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 735      * values will be rounded to their nearest pixel boundaries and the
 736      * width/height values will be ceiled to the next pixel boundary.
 737      *
 738      * @param child the child being positioned within this skin
 739      * @param areaX the horizontal offset of the layout area relative to this skin
 740      * @param areaY the vertical offset of the layout area relative to this skin
 741      * @param areaWidth  the width of the layout area
 742      * @param areaHeight the height of the layout area
 743      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 744      * @param halignment the horizontal alignment for the child within the area
 745      * @param valignment the vertical alignment for the child within the area
 746      *
 747      */
 748     protected void layoutInArea(Node child, double areaX, double areaY,
 749                                double areaWidth, double areaHeight,
 750                                double areaBaselineOffset,
 751                                HPos halignment, VPos valignment) {
 752         layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset,
 753                 Insets.EMPTY, true, true, halignment, valignment);
 754     }
 755 
 756     /**
 757      * Utility method which lays out the child within an area of this
 758      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 759      * with a baseline offset relative to that area.
 760      * <p>
 761      * If the child is resizable, this method will resize it to fill the specified
 762      * area unless the node's maximum size prevents it.  If the node's maximum
 763      * size preference is less than the area size, the maximum size will be used.
 764      * If node's maximum is greater than the area size, then the node will be
 765      * resized to fit within the area, unless its minimum size prevents it.
 766      * <p>
 767      * If the child has a non-null contentBias, then this method will use it when
 768      * resizing the child.  If the contentBias is horizontal, it will set its width
 769      * first to the area's width (up to the child's max width limit) and then pass
 770      * that value to compute the child's height.  If child's contentBias is vertical,
 771      * then it will set its height to the area height (up to child's max height limit)
 772      * and pass that height to compute the child's width.  If the child's contentBias
 773      * is null, then it's width and height have no dependencies on each other.
 774      * <p>
 775      * If the child is not resizable (Shape, Group, etc) then it will only be
 776      * positioned and not resized.
 777      * <p>
 778      * If the child's resulting size differs from the area's size (either
 779      * because it was not resizable or it's sizing preferences prevented it), then
 780      * this function will align the node relative to the area using horizontal and
 781      * vertical alignment values.
 782      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 783      * with the area baseline offset parameter, otherwise the baseline parameter
 784      * is ignored.
 785      * <p>
 786      * If {@code margin} is non-null, then that space will be allocated around the
 787      * child within the layout area.  margin may be null.
 788      * <p>
 789      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 790      * values will be rounded to their nearest pixel boundaries and the
 791      * width/height values will be ceiled to the next pixel boundary.
 792      *
 793      * @param child the child being positioned within this skin
 794      * @param areaX the horizontal offset of the layout area relative to this skin
 795      * @param areaY the vertical offset of the layout area relative to this skin
 796      * @param areaWidth  the width of the layout area
 797      * @param areaHeight the height of the layout area
 798      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 799      * @param margin the margin of space to be allocated around the child
 800      * @param halignment the horizontal alignment for the child within the area
 801      * @param valignment the vertical alignment for the child within the area
 802      */
 803     protected void layoutInArea(Node child, double areaX, double areaY,
 804                                double areaWidth, double areaHeight,
 805                                double areaBaselineOffset,
 806                                Insets margin,
 807                                HPos halignment, VPos valignment) {
 808         layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset,
 809                 margin, true, true, halignment, valignment);
 810     }
 811 
 812     /**
 813      * Utility method which lays out the child within an area of this
 814      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 815      * with a baseline offset relative to that area.
 816      * <p>
 817      * If the child is resizable, this method will use {@code fillWidth} and {@code fillHeight}
 818      * to determine whether to resize it to fill the area or keep the child at its
 819      * preferred dimension.  If fillWidth/fillHeight are true, then this method
 820      * will only resize the child up to its max size limits.  If the node's maximum
 821      * size preference is less than the area size, the maximum size will be used.
 822      * If node's maximum is greater than the area size, then the node will be
 823      * resized to fit within the area, unless its minimum size prevents it.
 824      * <p>
 825      * If the child has a non-null contentBias, then this method will use it when
 826      * resizing the child.  If the contentBias is horizontal, it will set its width
 827      * first and then pass that value to compute the child's height.  If child's
 828      * contentBias is vertical, then it will set its height first
 829      * and pass that value to compute the child's width.  If the child's contentBias
 830      * is null, then it's width and height have no dependencies on each other.
 831      * <p>
 832      * If the child is not resizable (Shape, Group, etc) then it will only be
 833      * positioned and not resized.
 834      * <p>
 835      * If the child's resulting size differs from the area's size (either
 836      * because it was not resizable or it's sizing preferences prevented it), then
 837      * this function will align the node relative to the area using horizontal and
 838      * vertical alignment values.
 839      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 840      * with the area baseline offset parameter, otherwise the baseline parameter
 841      * is ignored.
 842      * <p>
 843      * If {@code margin} is non-null, then that space will be allocated around the
 844      * child within the layout area.  margin may be null.
 845      * <p>
 846      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 847      * values will be rounded to their nearest pixel boundaries and the
 848      * width/height values will be ceiled to the next pixel boundary.
 849      *
 850      * @param child the child being positioned within this skin
 851      * @param areaX the horizontal offset of the layout area relative to this skin
 852      * @param areaY the vertical offset of the layout area relative to this skin
 853      * @param areaWidth  the width of the layout area
 854      * @param areaHeight the height of the layout area
 855      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 856      * @param margin the margin of space to be allocated around the child
 857      * @param fillWidth whether or not the child should be resized to fill the area width or kept to its preferred width
 858      * @param fillHeight whether or not the child should e resized to fill the area height or kept to its preferred height
 859      * @param halignment the horizontal alignment for the child within the area
 860      * @param valignment the vertical alignment for the child within the area
 861      */
 862     protected void layoutInArea(Node child, double areaX, double areaY,
 863                                double areaWidth, double areaHeight,
 864                                double areaBaselineOffset,
 865                                Insets margin, boolean fillWidth, boolean fillHeight,
 866                                HPos halignment, VPos valignment) {
 867         Region.layoutInArea(child, areaX, areaY, areaWidth, areaHeight,
 868                 areaBaselineOffset, margin, fillWidth, fillHeight, halignment,
 869                 valignment, control.isSnapToPixel());
 870     }
 871 
 872 
 873 
 874     /***************************************************************************
 875      *                                                                         *
 876      * Private Implementation                                                  *
 877      *                                                                         *
 878      **************************************************************************/
 879 
 880 
 881 
 882      /**************************************************************************
 883       *                                                                        *
 884       * Specialization of CSS handling code                                    *
 885       *                                                                        *
 886      **************************************************************************/
 887 
 888     private static class StyleableProperties {
 889         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 890 
 891         static {
 892             STYLEABLES = Collections.unmodifiableList(Control.getClassCssMetaData());
 893         }
 894     }
 895 
 896     /**
 897      * Returns the CssMetaData associated with this class, which may include the
 898      * CssMetaData of its superclasses.
 899      * @return the CssMetaData associated with this class, which may include the
 900      * CssMetaData of its superclasses
 901      */
 902     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 903         return SkinBase.StyleableProperties.STYLEABLES;
 904     }
 905 
 906     /**
 907      * This method should delegate to {@link Node#getClassCssMetaData()} so that
 908      * a Node's CssMetaData can be accessed without the need for reflection.
 909      * @return The CssMetaData associated with this node, which may include the
 910      * CssMetaData of its superclasses.
 911      */
 912     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
 913         return getClassCssMetaData();
 914     }
 915 
 916     /**
 917      * Used to specify that a pseudo-class of this Node has changed. If the
 918      * pseudo-class is used in a CSS selector that matches this Node, CSS will
 919      * be reapplied. Typically, this method is called from the {@code invalidated}
 920      * method of a property that is used as a pseudo-class. For example:
 921      * <pre><code>
 922      *
 923      *     private static final PseudoClass MY_PSEUDO_CLASS_STATE = PseudoClass.getPseudoClass("my-state");
 924      *
 925      *     BooleanProperty myPseudoClassState = new BooleanPropertyBase(false) {
 926      *
 927      *           {@literal @}Override public void invalidated() {
 928      *                pseudoClassStateChanged(MY_PSEUDO_CLASS_STATE, get());
 929      *           }
 930      *
 931      *           {@literal @}Override public Object getBean() {
 932      *               return MyControl.this;
 933      *           }
 934      *
 935      *           {@literal @}Override public String getName() {
 936      *               return "myPseudoClassState";
 937      *           }
 938      *       };
 939      * </code></pre>
 940      *
 941      * @see Node#pseudoClassStateChanged
 942      * @param pseudoClass the pseudo-class that has changed state
 943      * @param active whether or not the state is active
 944      * @since JavaFX 8.0
 945      */
 946     public final void pseudoClassStateChanged(PseudoClass pseudoClass, boolean active) {
 947         Control ctl = getSkinnable();
 948         if (ctl != null) {
 949             ctl.pseudoClassStateChanged(pseudoClass, active);
 950         }
 951     }
 952 
 953 
 954     /***************************************************************************
 955      *                                                                         *
 956      * Accessibility handling                                                  *
 957      *                                                                         *
 958      **************************************************************************/
 959 
 960     /**
 961      * This method is called by the assistive technology to request
 962      * the value for an attribute.
 963      * <p>
 964      * This method is commonly overridden by subclasses to implement
 965      * attributes that are required for a specific role.<br>
 966      * If a particular attribute is not handled, the superclass implementation
 967      * must be called.
 968      * </p>
 969      *
 970      * @param attribute the requested attribute
 971      * @param parameters optional list of parameters
 972      * @return the value for the requested attribute
 973      *
 974      * @see AccessibleAttribute
 975      * @see Node#queryAccessibleAttribute
 976      *
 977      * @since JavaFX 8u40
 978      */
 979     protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 980         return null;
 981     }
 982 
 983     /**
 984      * This method is called by the assistive technology to request the action
 985      * indicated by the argument should be executed.
 986      * <p>
 987      * This method is commonly overridden by subclasses to implement
 988      * action that are required for a specific role.<br>
 989      * If a particular action is not handled, the superclass implementation
 990      * must be called.
 991      * </p>
 992      *
 993      * @param action the action to execute
 994      * @param parameters optional list of parameters
 995      *
 996      * @see AccessibleAction
 997      * @see Node#executeAccessibleAction
 998      *
 999      * @since JavaFX 8u40
1000      */
1001     protected void executeAccessibleAction(AccessibleAction action, Object... parameters) {
1002     }
1003 
1004     /***************************************************************************
1005      *                                                                         *
1006      * Testing-only API                                                        *
1007      *                                                                         *
1008      **************************************************************************/
1009 
1010 }