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      */
 172     public final ObservableList<Node> getChildren() {
 173         return children;
 174     }
 175 
 176     /**
 177      * Called during the layout pass of the scenegraph.
 178      */
 179     protected void layoutChildren(final double contentX, final double contentY,
 180             final double contentWidth, final double contentHeight) {
 181         // By default simply sizes all managed children to fit within the space provided
 182         for (int i=0, max=children.size(); i<max; i++) {
 183             Node child = children.get(i);
 184             if (child.isManaged()) {
 185                 layoutInArea(child, contentX, contentY, contentWidth, contentHeight, -1, HPos.CENTER, VPos.CENTER);
 186             }
 187         }
 188     }
 189 
 190     /**
 191      * Determines whether all mouse events should be automatically consumed.
 192      */
 193     protected final void consumeMouseEvents(boolean value) {
 194         if (value) {
 195             control.addEventHandler(MouseEvent.ANY, mouseEventConsumer);
 196         } else {
 197             control.removeEventHandler(MouseEvent.ANY, mouseEventConsumer);
 198         }
 199     }
 200 
 201 
 202     /**
 203      * Subclasses can invoke this method to register that they want to listen to
 204      * property change events for the given property. Registered {@link Consumer} instances
 205      * will be executed in the order in which they are registered.
 206      */
 207     protected final void registerChangeListener(ObservableValue<?> property, Consumer<ObservableValue<?>> consumer) {
 208         if (lambdaChangeListenerHandler == null) {
 209             lambdaChangeListenerHandler = new LambdaMultiplePropertyChangeListenerHandler();
 210         }
 211         lambdaChangeListenerHandler.registerChangeListener(property, consumer);
 212     }
 213 
 214     /**
 215      * Unregisters all change listeners that have been registered using {@link #registerChangeListener(ObservableValue, Consumer)}
 216      * for the given property. The end result is that the given property is no longer observed by any of the change
 217      * listeners, but it may still have additional listeners registered on it through means outside of
 218      * {@link #registerChangeListener(ObservableValue, Consumer)}.
 219      *
 220      * @param property The property for which all listeners should be removed.
 221      * @return A single chained {@link Consumer} consisting of all {@link Consumer consumers} registered through
 222      *      {@link #registerChangeListener(ObservableValue, Consumer)}. If no consumers have been registered on this
 223      *      property, null will be returned.
 224      * @since 9
 225      */
 226     protected final Consumer<ObservableValue<?>> unregisterChangeListeners(ObservableValue<?> property) {
 227         if (lambdaChangeListenerHandler == null) {
 228             return null;
 229         }
 230         return lambdaChangeListenerHandler.unregisterChangeListeners(property);
 231     }
 232 
 233 
 234 
 235 
 236     /***************************************************************************
 237      *                                                                         *
 238      * Public Layout-related API                                               *
 239      *                                                                         *
 240      **************************************************************************/
 241 
 242     /**
 243      * Computes the minimum allowable width of the Skin, based on the provided
 244      * height.
 245      *
 246      * @param height The height of the Skin, in case this value might dictate
 247      *      the minimum width.
 248      * @param topInset the pixel snapped top inset
 249      * @param rightInset the pixel snapped right inset
 250      * @param bottomInset the pixel snapped bottom inset
 251      * @param leftInset  the pixel snapped left inset
 252      * @return A double representing the minimum width of this Skin.
 253      */
 254     protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 255 
 256         double minX = 0;
 257         double maxX = 0;
 258         boolean firstManagedChild = true;
 259         for (int i = 0; i < children.size(); i++) {
 260             Node node = children.get(i);
 261             if (node.isManaged()) {
 262                 final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
 263                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 264                     minX = Math.min(minX, x);
 265                     maxX = Math.max(maxX, x + node.minWidth(-1));
 266                 } else {
 267                     minX = x;
 268                     maxX = x + node.minWidth(-1);
 269                     firstManagedChild = false;
 270                 }
 271             }
 272         }
 273         double minWidth = maxX - minX;
 274         return leftInset + minWidth + rightInset;
 275     }
 276 
 277     /**
 278      * Computes the minimum allowable height of the Skin, based on the provided
 279      * width.
 280      *
 281      * @param width The width of the Skin, in case this value might dictate
 282      *      the minimum height.
 283      * @param topInset the pixel snapped top inset
 284      * @param rightInset the pixel snapped right inset
 285      * @param bottomInset the pixel snapped bottom inset
 286      * @param leftInset  the pixel snapped left inset
 287      * @return A double representing the minimum height of this Skin.
 288      */
 289     protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 290 
 291         double minY = 0;
 292         double maxY = 0;
 293         boolean firstManagedChild = true;
 294         for (int i = 0; i < children.size(); i++) {
 295             Node node = children.get(i);
 296             if (node.isManaged()) {
 297                 final double y = node.getLayoutBounds().getMinY() + node.getLayoutY();
 298                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 299                     minY = Math.min(minY, y);
 300                     maxY = Math.max(maxY, y + node.minHeight(-1));
 301                 } else {
 302                     minY = y;
 303                     maxY = y + node.minHeight(-1);
 304                     firstManagedChild = false;
 305                 }
 306             }
 307         }
 308         double minHeight = maxY - minY;
 309         return topInset + minHeight + bottomInset;
 310     }
 311 
 312     /**
 313      * Computes the maximum allowable width of the Skin, based on the provided
 314      * height.
 315      *
 316      * @param height The height of the Skin, in case this value might dictate
 317      *      the maximum width.
 318      * @param topInset the pixel snapped top inset
 319      * @param rightInset the pixel snapped right inset
 320      * @param bottomInset the pixel snapped bottom inset
 321      * @param leftInset  the pixel snapped left inset
 322      * @return A double representing the maximum width of this Skin.
 323      */
 324     protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 325         return Double.MAX_VALUE;
 326     }
 327 
 328     /**
 329      * Computes the maximum allowable height of the Skin, based on the provided
 330      * width.
 331      *
 332      * @param width The width of the Skin, in case this value might dictate
 333      *      the maximum height.
 334      * @param topInset the pixel snapped top inset
 335      * @param rightInset the pixel snapped right inset
 336      * @param bottomInset the pixel snapped bottom inset
 337      * @param leftInset  the pixel snapped left inset
 338      * @return A double representing the maximum height of this Skin.
 339      */
 340     protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 341         return Double.MAX_VALUE;
 342     }
 343 
 344     // PENDING_DOC_REVIEW
 345     /**
 346      * Calculates the preferred width of this {@code SkinBase}. The default
 347      * implementation calculates this width as the width of the area occupied
 348      * by its managed children when they are positioned at their
 349      * current positions at their preferred widths.
 350      *
 351      * @param height the height that should be used if preferred width depends on it
 352      * @param topInset the pixel snapped top inset
 353      * @param rightInset the pixel snapped right inset
 354      * @param bottomInset the pixel snapped bottom inset
 355      * @param leftInset  the pixel snapped left inset
 356      * @return the calculated preferred width
 357      */
 358     protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 359 
 360         double minX = 0;
 361         double maxX = 0;
 362         boolean firstManagedChild = true;
 363         for (int i = 0; i < children.size(); i++) {
 364             Node node = children.get(i);
 365             if (node.isManaged()) {
 366                 final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
 367                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 368                     minX = Math.min(minX, x);
 369                     maxX = Math.max(maxX, x + node.prefWidth(-1));
 370                 } else {
 371                     minX = x;
 372                     maxX = x + node.prefWidth(-1);
 373                     firstManagedChild = false;
 374                 }
 375             }
 376         }
 377         return maxX - minX;
 378     }
 379 
 380     // PENDING_DOC_REVIEW
 381     /**
 382      * Calculates the preferred height of this {@code SkinBase}. The default
 383      * implementation calculates this height as the height of the area occupied
 384      * by its managed children when they are positioned at their current
 385      * positions at their preferred heights.
 386      *
 387      * @param width the width that should be used if preferred height depends on it
 388      * @param topInset the pixel snapped top inset
 389      * @param rightInset the pixel snapped right inset
 390      * @param bottomInset the pixel snapped bottom inset
 391      * @param leftInset  the pixel snapped left inset
 392      * @return the calculated preferred height
 393      */
 394     protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 395 
 396         double minY = 0;
 397         double maxY = 0;
 398         boolean firstManagedChild = true;
 399         for (int i = 0; i < children.size(); i++) {
 400             Node node = children.get(i);
 401             if (node.isManaged()) {
 402                 final double y = node.getLayoutBounds().getMinY() + node.getLayoutY();
 403                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 404                     minY = Math.min(minY, y);
 405                     maxY = Math.max(maxY, y + node.prefHeight(-1));
 406                 } else {
 407                     minY = y;
 408                     maxY = y + node.prefHeight(-1);
 409                     firstManagedChild = false;
 410                 }
 411             }
 412         }
 413         return maxY - minY;
 414     }
 415 
 416     /**
 417      * Calculates the baseline offset based on the first managed child. If there
 418      * is no such child, returns {@link Node#getBaselineOffset()}.
 419      *
 420      * @param topInset the pixel snapped top inset
 421      * @param rightInset the pixel snapped right inset
 422      * @param bottomInset the pixel snapped bottom inset
 423      * @param leftInset  the pixel snapped left inset
 424      * @return baseline offset
 425      */
 426     protected double computeBaselineOffset(double topInset, double rightInset, double bottomInset, double leftInset) {
 427         int size = children.size();
 428         for (int i = 0; i < size; ++i) {
 429             Node child = children.get(i);
 430             if (child.isManaged()) {
 431                 double offset = child.getBaselineOffset();
 432                 if (offset == Node.BASELINE_OFFSET_SAME_AS_HEIGHT) {
 433                     continue;
 434                 }
 435                 return child.getLayoutBounds().getMinY() + child.getLayoutY() + offset;
 436             }
 437         }
 438         return Node.BASELINE_OFFSET_SAME_AS_HEIGHT;
 439     }
 440 
 441 
 442     /***************************************************************************
 443      *                                                                         *
 444      * (Mostly ugly) Skin -> Control forwarding API                            *
 445      *                                                                         *
 446      **************************************************************************/
 447 
 448     /**
 449      * Utility method to get the top inset which includes padding and border
 450      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 451      *
 452      * @return Rounded up insets top
 453      */
 454     protected double snappedTopInset() {
 455         return control.snappedTopInset();
 456     }
 457 
 458     /**
 459      * Utility method to get the bottom inset which includes padding and border
 460      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 461      *
 462      * @return Rounded up insets bottom
 463      */
 464     protected double snappedBottomInset() {
 465         return control.snappedBottomInset();
 466     }
 467 
 468     /**
 469      * Utility method to get the left inset which includes padding and border
 470      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 471      *
 472      * @return Rounded up insets left
 473      */
 474     protected double snappedLeftInset() {
 475         return control.snappedLeftInset();
 476     }
 477 
 478     /**
 479      * Utility method to get the right inset which includes padding and border
 480      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 481      *
 482      * @return Rounded up insets right
 483      */
 484     protected double snappedRightInset() {
 485         return control.snappedRightInset();
 486     }
 487 
 488     /**
 489      * If this region's snapToPixel property is false, this method returns the
 490      * same value, else it tries to return a value rounded to the nearest
 491      * pixel, but since there is no indication if the value is a vertical
 492      * or horizontal measurement then it may be snapped to the wrong pixel
 493      * size metric on screens with different horizontal and vertical scales.
 494      * @param value the space value to be snapped
 495      * @return value rounded to nearest pixel
 496      * @deprecated replaced by {@code snapSpaceX()} and {@code snapSpaceY()}
 497      */
 498     protected double snapSpace(double value) {
 499         return control.snapSpaceX(value);
 500     }
 501 
 502     /**
 503      * If this region's snapToPixel property is true, returns a value rounded
 504      * to the nearest pixel in the horizontal direction, else returns the
 505      * same value.
 506      * @param value the space value to be snapped
 507      * @return value rounded to nearest pixel
 508      * @since 9
 509      */
 510     protected double snapSpaceX(double value) {
 511         return control.snapSpaceX(value);
 512     }
 513 
 514     /**
 515      * If this region's snapToPixel property is true, returns a value rounded
 516      * to the nearest pixel in the vertical direction, else returns the
 517      * same value.
 518      * @param value the space value to be snapped
 519      * @return value rounded to nearest pixel
 520      * @since 9
 521      */
 522     protected double snapSpaceY(double value) {
 523         return control.snapSpaceY(value);
 524     }
 525 
 526     /**
 527     /**
 528      * If this region's snapToPixel property is false, this method returns the
 529      * same value, else it tries to return a value ceiled to the nearest
 530      * pixel, but since there is no indication if the value is a vertical
 531      * or horizontal measurement then it may be snapped to the wrong pixel
 532      * size metric on screens with different horizontal and vertical scales.
 533      * @param value the size value to be snapped
 534      * @return value ceiled to nearest pixel
 535      * @deprecated replaced by {@code snapSizeX()} and {@code snapSizeY()}
 536      */
 537     protected double snapSize(double value) {
 538         return control.snapSizeX(value);
 539     }
 540 
 541     /**
 542      * If this region's snapToPixel property is true, returns a value ceiled
 543      * to the nearest pixel in the horizontal direction, else returns the
 544      * same value.
 545      * @param value the size value to be snapped
 546      * @return value ceiled to nearest pixel
 547      * @since 9
 548      */
 549     protected double snapSizeX(double value) {
 550         return control.snapSizeX(value);
 551     }
 552 
 553     /**
 554      * If this region's snapToPixel property is true, returns a value ceiled
 555      * to the nearest pixel in the vertical direction, else returns the
 556      * same value.
 557      * @param value the size value to be snapped
 558      * @return value ceiled to nearest pixel
 559      * @since 9
 560      */
 561     protected double snapSizeY(double value) {
 562         return control.snapSizeY(value);
 563     }
 564 
 565     /**
 566      * If this region's snapToPixel property is false, this method returns the
 567      * same value, else it tries to return a value rounded to the nearest
 568      * pixel, but since there is no indication if the value is a vertical
 569      * or horizontal measurement then it may be snapped to the wrong pixel
 570      * size metric on screens with different horizontal and vertical scales.
 571      * @param value the position value to be snapped
 572      * @return value rounded to nearest pixel
 573      * @deprecated replaced by {@code snapPositionX()} and {@code snapPositionY()}
 574      */
 575     protected double snapPosition(double value) {
 576         return control.snapPositionX(value);
 577     }
 578 
 579     /**
 580      * If this region's snapToPixel property is true, returns a value rounded
 581      * to the nearest pixel in the horizontal direction, else returns the
 582      * same value.
 583      * @param value the position value to be snapped
 584      * @return value rounded to nearest pixel
 585      * @since 9
 586      */
 587     protected double snapPositionX(double value) {
 588         return control.snapPositionX(value);
 589     }
 590 
 591     /**
 592      * If this region's snapToPixel property is true, returns a value rounded
 593      * to the nearest pixel in the vertical direction, else returns the
 594      * same value.
 595      * @param value the position value to be snapped
 596      * @return value rounded to nearest pixel
 597      * @since 9
 598      */
 599     protected double snapPositionY(double value) {
 600         return control.snapPositionY(value);
 601     }
 602 
 603     /**
 604      * Utility method which positions the child within an area of this
 605      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 606      * with a baseline offset relative to that area.
 607      * <p>
 608      * This function does <i>not</i> resize the node and uses the node's layout bounds
 609      * width and height to determine how it should be positioned within the area.
 610      * <p>
 611      * If the vertical alignment is {@code VPos.BASELINE} then it
 612      * will position the node so that its own baseline aligns with the passed in
 613      * {@code baselineOffset}, otherwise the baseline parameter is ignored.
 614      * <p>
 615      * If {@code snapToPixel} is {@code true} for this skin, then the x/y position
 616      * values will be rounded to their nearest pixel boundaries.
 617      *
 618      * @param child the child being positioned within this skin
 619      * @param areaX the horizontal offset of the layout area relative to this skin
 620      * @param areaY the vertical offset of the layout area relative to this skin
 621      * @param areaWidth  the width of the layout area
 622      * @param areaHeight the height of the layout area
 623      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 624      * @param halignment the horizontal alignment for the child within the area
 625      * @param valignment the vertical alignment for the child within the area
 626      *
 627      */
 628     protected void positionInArea(Node child, double areaX, double areaY,
 629             double areaWidth, double areaHeight, double areaBaselineOffset,
 630             HPos halignment, VPos valignment) {
 631         positionInArea(child, areaX, areaY, areaWidth, areaHeight,
 632                 areaBaselineOffset, Insets.EMPTY, halignment, valignment);
 633     }
 634 
 635     /**
 636      * Utility method which positions the child within an area of this
 637      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 638      * with a baseline offset relative to that area.
 639      * <p>
 640      * This function does <i>not</i> resize the node and uses the node's layout bounds
 641      * width and height to determine how it should be positioned within the area.
 642      * <p>
 643      * If the vertical alignment is {@code VPos.BASELINE} then it
 644      * will position the node so that its own baseline aligns with the passed in
 645      * {@code baselineOffset},  otherwise the baseline parameter is ignored.
 646      * <p>
 647      * If {@code snapToPixel} is {@code true} for this skin, then the x/y position
 648      * values will be rounded to their nearest pixel boundaries.
 649      * <p>
 650      * If {@code margin} is non-null, then that space will be allocated around the
 651      * child within the layout area.  margin may be null.
 652      *
 653      * @param child the child being positioned within this skin
 654      * @param areaX the horizontal offset of the layout area relative to this skin
 655      * @param areaY the vertical offset of the layout area relative to this skin
 656      * @param areaWidth  the width of the layout area
 657      * @param areaHeight the height of the layout area
 658      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 659      * @param margin the margin of space to be allocated around the child
 660      * @param halignment the horizontal alignment for the child within the area
 661      * @param valignment the vertical alignment for the child within the area
 662      *
 663      * @since JavaFX 8.0
 664      */
 665     protected void positionInArea(Node child, double areaX, double areaY,
 666             double areaWidth, double areaHeight, double areaBaselineOffset,
 667             Insets margin, HPos halignment, VPos valignment) {
 668         Region.positionInArea(child, areaX, areaY, areaWidth, areaHeight,
 669                 areaBaselineOffset, margin, halignment, valignment,
 670                 control.isSnapToPixel());
 671     }
 672 
 673     /**
 674      * Utility method which lays out the child within an area of this
 675      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 676      * with a baseline offset relative to that area.
 677      * <p>
 678      * If the child is resizable, this method will resize it to fill the specified
 679      * area unless the node's maximum size prevents it.  If the node's maximum
 680      * size preference is less than the area size, the maximum size will be used.
 681      * If node's maximum is greater than the area size, then the node will be
 682      * resized to fit within the area, unless its minimum size prevents it.
 683      * <p>
 684      * If the child has a non-null contentBias, then this method will use it when
 685      * resizing the child.  If the contentBias is horizontal, it will set its width
 686      * first to the area's width (up to the child's max width limit) and then pass
 687      * that value to compute the child's height.  If child's contentBias is vertical,
 688      * then it will set its height to the area height (up to child's max height limit)
 689      * and pass that height to compute the child's width.  If the child's contentBias
 690      * is null, then it's width and height have no dependencies on each other.
 691      * <p>
 692      * If the child is not resizable (Shape, Group, etc) then it will only be
 693      * positioned and not resized.
 694      * <p>
 695      * If the child's resulting size differs from the area's size (either
 696      * because it was not resizable or it's sizing preferences prevented it), then
 697      * this function will align the node relative to the area using horizontal and
 698      * vertical alignment values.
 699      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 700      * with the area baseline offset parameter, otherwise the baseline parameter
 701      * is ignored.
 702      * <p>
 703      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 704      * values will be rounded to their nearest pixel boundaries and the
 705      * width/height values will be ceiled to the next pixel boundary.
 706      *
 707      * @param child the child being positioned within this skin
 708      * @param areaX the horizontal offset of the layout area relative to this skin
 709      * @param areaY the vertical offset of the layout area relative to this skin
 710      * @param areaWidth  the width of the layout area
 711      * @param areaHeight the height of the layout area
 712      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 713      * @param halignment the horizontal alignment for the child within the area
 714      * @param valignment the vertical alignment for the child within the area
 715      *
 716      */
 717     protected void layoutInArea(Node child, double areaX, double areaY,
 718                                double areaWidth, double areaHeight,
 719                                double areaBaselineOffset,
 720                                HPos halignment, VPos valignment) {
 721         layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset,
 722                 Insets.EMPTY, true, true, halignment, valignment);
 723     }
 724 
 725     /**
 726      * Utility method which lays out the child within an area of this
 727      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 728      * with a baseline offset relative to that area.
 729      * <p>
 730      * If the child is resizable, this method will resize it to fill the specified
 731      * area unless the node's maximum size prevents it.  If the node's maximum
 732      * size preference is less than the area size, the maximum size will be used.
 733      * If node's maximum is greater than the area size, then the node will be
 734      * resized to fit within the area, unless its minimum size prevents it.
 735      * <p>
 736      * If the child has a non-null contentBias, then this method will use it when
 737      * resizing the child.  If the contentBias is horizontal, it will set its width
 738      * first to the area's width (up to the child's max width limit) and then pass
 739      * that value to compute the child's height.  If child's contentBias is vertical,
 740      * then it will set its height to the area height (up to child's max height limit)
 741      * and pass that height to compute the child's width.  If the child's contentBias
 742      * is null, then it's width and height have no dependencies on each other.
 743      * <p>
 744      * If the child is not resizable (Shape, Group, etc) then it will only be
 745      * positioned and not resized.
 746      * <p>
 747      * If the child's resulting size differs from the area's size (either
 748      * because it was not resizable or it's sizing preferences prevented it), then
 749      * this function will align the node relative to the area using horizontal and
 750      * vertical alignment values.
 751      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 752      * with the area baseline offset parameter, otherwise the baseline parameter
 753      * is ignored.
 754      * <p>
 755      * If {@code margin} is non-null, then that space will be allocated around the
 756      * child within the layout area.  margin may be null.
 757      * <p>
 758      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 759      * values will be rounded to their nearest pixel boundaries and the
 760      * width/height values will be ceiled to the next pixel boundary.
 761      *
 762      * @param child the child being positioned within this skin
 763      * @param areaX the horizontal offset of the layout area relative to this skin
 764      * @param areaY the vertical offset of the layout area relative to this skin
 765      * @param areaWidth  the width of the layout area
 766      * @param areaHeight the height of the layout area
 767      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 768      * @param margin the margin of space to be allocated around the child
 769      * @param halignment the horizontal alignment for the child within the area
 770      * @param valignment the vertical alignment for the child within the area
 771      */
 772     protected void layoutInArea(Node child, double areaX, double areaY,
 773                                double areaWidth, double areaHeight,
 774                                double areaBaselineOffset,
 775                                Insets margin,
 776                                HPos halignment, VPos valignment) {
 777         layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset,
 778                 margin, true, true, halignment, valignment);
 779     }
 780 
 781     /**
 782      * Utility method which lays out the child within an area of this
 783      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 784      * with a baseline offset relative to that area.
 785      * <p>
 786      * If the child is resizable, this method will use {@code fillWidth} and {@code fillHeight}
 787      * to determine whether to resize it to fill the area or keep the child at its
 788      * preferred dimension.  If fillWidth/fillHeight are true, then this method
 789      * will only resize the child up to its max size limits.  If the node's maximum
 790      * size preference is less than the area size, the maximum size will be used.
 791      * If node's maximum is greater than the area size, then the node will be
 792      * resized to fit within the area, unless its minimum size prevents it.
 793      * <p>
 794      * If the child has a non-null contentBias, then this method will use it when
 795      * resizing the child.  If the contentBias is horizontal, it will set its width
 796      * first and then pass that value to compute the child's height.  If child's
 797      * contentBias is vertical, then it will set its height first
 798      * and pass that value to compute the child's width.  If the child's contentBias
 799      * is null, then it's width and height have no dependencies on each other.
 800      * <p>
 801      * If the child is not resizable (Shape, Group, etc) then it will only be
 802      * positioned and not resized.
 803      * <p>
 804      * If the child's resulting size differs from the area's size (either
 805      * because it was not resizable or it's sizing preferences prevented it), then
 806      * this function will align the node relative to the area using horizontal and
 807      * vertical alignment values.
 808      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 809      * with the area baseline offset parameter, otherwise the baseline parameter
 810      * is ignored.
 811      * <p>
 812      * If {@code margin} is non-null, then that space will be allocated around the
 813      * child within the layout area.  margin may be null.
 814      * <p>
 815      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 816      * values will be rounded to their nearest pixel boundaries and the
 817      * width/height values will be ceiled to the next pixel boundary.
 818      *
 819      * @param child the child being positioned within this skin
 820      * @param areaX the horizontal offset of the layout area relative to this skin
 821      * @param areaY the vertical offset of the layout area relative to this skin
 822      * @param areaWidth  the width of the layout area
 823      * @param areaHeight the height of the layout area
 824      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 825      * @param margin the margin of space to be allocated around the child
 826      * @param fillWidth whether or not the child should be resized to fill the area width or kept to its preferred width
 827      * @param fillHeight whether or not the child should e resized to fill the area height or kept to its preferred height
 828      * @param halignment the horizontal alignment for the child within the area
 829      * @param valignment the vertical alignment for the child within the area
 830      */
 831     protected void layoutInArea(Node child, double areaX, double areaY,
 832                                double areaWidth, double areaHeight,
 833                                double areaBaselineOffset,
 834                                Insets margin, boolean fillWidth, boolean fillHeight,
 835                                HPos halignment, VPos valignment) {
 836         Region.layoutInArea(child, areaX, areaY, areaWidth, areaHeight,
 837                 areaBaselineOffset, margin, fillWidth, fillHeight, halignment,
 838                 valignment, control.isSnapToPixel());
 839     }
 840 
 841 
 842 
 843     /***************************************************************************
 844      *                                                                         *
 845      * Private Implementation                                                  *
 846      *                                                                         *
 847      **************************************************************************/
 848 
 849 
 850 
 851      /**************************************************************************
 852       *                                                                        *
 853       * Specialization of CSS handling code                                    *
 854       *                                                                        *
 855      **************************************************************************/
 856 
 857     private static class StyleableProperties {
 858         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 859 
 860         static {
 861             STYLEABLES = Collections.unmodifiableList(Control.getClassCssMetaData());
 862         }
 863     }
 864 
 865     /**
 866      * Returns the CssMetaData associated with this class, which may include the
 867      * CssMetaData of its super classes.
 868      */
 869     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 870         return SkinBase.StyleableProperties.STYLEABLES;
 871     }
 872 
 873     /**
 874      * This method should delegate to {@link Node#getClassCssMetaData()} so that
 875      * a Node's CssMetaData can be accessed without the need for reflection.
 876      * @return The CssMetaData associated with this node, which may include the
 877      * CssMetaData of its super classes.
 878      */
 879     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
 880         return getClassCssMetaData();
 881     }
 882 
 883     /**
 884      * Used to specify that a pseudo-class of this Node has changed. If the
 885      * pseudo-class is used in a CSS selector that matches this Node, CSS will
 886      * be reapplied. Typically, this method is called from the {@code invalidated}
 887      * method of a property that is used as a pseudo-class. For example:
 888      * <code><pre>
 889      *
 890      *     private static final PseudoClass MY_PSEUDO_CLASS_STATE = PseudoClass.getPseudoClass("my-state");
 891      *
 892      *     BooleanProperty myPseudoClassState = new BooleanPropertyBase(false) {
 893      *
 894      *           {@literal @}Override public void invalidated() {
 895      *                pseudoClassStateChanged(MY_PSEUDO_CLASS_STATE, get());
 896      *           }
 897      *
 898      *           {@literal @}Override public Object getBean() {
 899      *               return MyControl.this;
 900      *           }
 901      *
 902      *           {@literal @}Override public String getName() {
 903      *               return "myPseudoClassState";
 904      *           }
 905      *       };
 906      * </pre><code>
 907      *
 908      * @see Node#pseudoClassStateChanged
 909      * @param pseudoClass the pseudo-class that has changed state
 910      * @param active whether or not the state is active
 911      * @since JavaFX 8.0
 912      */
 913     public final void pseudoClassStateChanged(PseudoClass pseudoClass, boolean active) {
 914         Control ctl = getSkinnable();
 915         if (ctl != null) {
 916             ctl.pseudoClassStateChanged(pseudoClass, active);
 917         }
 918     }
 919 
 920 
 921     /***************************************************************************
 922      *                                                                         *
 923      * Accessibility handling                                                  *
 924      *                                                                         *
 925      **************************************************************************/
 926 
 927     /**
 928      * This method is called by the assistive technology to request
 929      * the value for an attribute.
 930      * <p>
 931      * This method is commonly overridden by subclasses to implement
 932      * attributes that are required for a specific role.<br>
 933      * If a particular attribute is not handled, the super class implementation
 934      * must be called.
 935      * </p>
 936      *
 937      * @param attribute the requested attribute
 938      * @param parameters optional list of parameters
 939      * @return the value for the requested attribute
 940      *
 941      * @see AccessibleAttribute
 942      * @see Node#queryAccessibleAttribute
 943      *
 944      * @since JavaFX 8u40
 945      */
 946     protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 947         return null;
 948     }
 949 
 950     /**
 951      * This method is called by the assistive technology to request the action
 952      * indicated by the argument should be executed.
 953      * <p>
 954      * This method is commonly overridden by subclasses to implement
 955      * action that are required for a specific role.<br>
 956      * If a particular action is not handled, the super class implementation
 957      * must be called.
 958      * </p>
 959      *
 960      * @param action the action to execute
 961      * @param parameters optional list of parameters
 962      *
 963      * @see AccessibleAction
 964      * @see Node#executeAccessibleAction
 965      *
 966      * @since JavaFX 8u40
 967      */
 968     protected void executeAccessibleAction(AccessibleAction action, Object... parameters) {
 969     }
 970 
 971     /***************************************************************************
 972      *                                                                         *
 973      * Testing-only API                                                        *
 974      *                                                                         *
 975      **************************************************************************/
 976 
 977 }