1 /*
   2  * Copyright (c) 2012, 2014, 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 javafx.css.CssMetaData;
  29 import javafx.css.PseudoClass;
  30 import java.util.Collections;
  31 import java.util.List;
  32 import javafx.collections.ObservableList;
  33 import javafx.css.Styleable;
  34 import javafx.event.EventHandler;
  35 import javafx.geometry.HPos;
  36 import javafx.geometry.Insets;
  37 import javafx.geometry.VPos;
  38 import javafx.scene.AccessibleAction;
  39 import javafx.scene.AccessibleAttribute;
  40 import javafx.scene.Node;
  41 import javafx.scene.input.MouseEvent;
  42 import javafx.scene.layout.Region;
  43 
  44 /**
  45  * Base implementation class for defining the visual representation of user
  46  * interface controls by defining a scene graph of nodes to represent the
  47  * {@link Skin skin}.
  48  * A user interface control is abstracted behind the {@link Skinnable} interface.
  49  *
  50  * @since JavaFX 8.0
  51  */
  52 public abstract class SkinBase<C extends Control> implements Skin<C> {
  53     
  54     /***************************************************************************
  55      *                                                                         *
  56      * Private fields                                                          *
  57      *                                                                         *
  58      **************************************************************************/
  59 
  60     /**
  61      * The {@code Control} that is referencing this Skin. There is a
  62      * one-to-one relationship between a {@code Skin} and a {@code Control}.
  63      * When a {@code Skin} is set on a {@code Control}, this variable is
  64      * automatically updated.
  65      */
  66     private C control;
  67     
  68     /**
  69      * A local field that directly refers to the children list inside the Control.
  70      */
  71     private ObservableList<Node> children;
  72     
  73     
  74     
  75     /***************************************************************************
  76      *                                                                         *
  77      * Event Handlers / Listeners                                              *
  78      *                                                                         *
  79      **************************************************************************/
  80     
  81     /**
  82      * Mouse handler used for consuming all mouse events (preventing them
  83      * from bubbling up to parent)
  84      */
  85     private static final EventHandler<MouseEvent> mouseEventConsumer = event -> {
  86         /*
  87         ** we used to consume mouse wheel rotations here,
  88         ** be we've switched to ScrollEvents, and only consume those which we use.
  89         ** See RT-13995 & RT-14480
  90         */
  91         event.consume();
  92     };
  93     
  94     
  95     
  96     /***************************************************************************
  97      *                                                                         *
  98      * Constructor                                                             *
  99      *                                                                         *
 100      **************************************************************************/
 101 
 102     /**
 103      * Constructor for all SkinBase instances.
 104      * 
 105      * @param control The control for which this Skin should attach to.
 106      */
 107     protected SkinBase(final C control) {
 108         if (control == null) {
 109             throw new IllegalArgumentException("Cannot pass null for control");
 110         }
 111 
 112         // Update the control and behavior
 113         this.control = control;
 114         this.children = control.getControlChildren();
 115         
 116         // Default behavior for controls is to consume all mouse events
 117         consumeMouseEvents(true);
 118     }
 119     
 120     
 121 
 122     /***************************************************************************
 123      *                                                                         *
 124      * Public API (from Skin)                                                  *
 125      *                                                                         *
 126      **************************************************************************/    
 127 
 128     /** {@inheritDoc} */
 129     @Override public final C getSkinnable() {
 130         return control;
 131     }
 132 
 133     /** {@inheritDoc} */
 134     @Override public final Node getNode() {
 135         return control; 
 136     }
 137 
 138     /** {@inheritDoc} */
 139     @Override public void dispose() { 
 140 //        control.removeEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler);
 141 
 142         this.control = null;
 143     }
 144     
 145     
 146     
 147     /***************************************************************************
 148      *                                                                         *
 149      * Public API                                                              *
 150      *                                                                         *
 151      **************************************************************************/     
 152     
 153     /**
 154      * Returns the children of the skin.
 155      */
 156     public final ObservableList<Node> getChildren() {
 157         return children;
 158     }
 159     
 160     /**
 161      * Called during the layout pass of the scenegraph. 
 162      */
 163     protected void layoutChildren(final double contentX, final double contentY,
 164             final double contentWidth, final double contentHeight) {
 165         // By default simply sizes all managed children to fit within the space provided
 166         for (int i=0, max=children.size(); i<max; i++) {
 167             Node child = children.get(i);
 168             if (child.isManaged()) {
 169                 layoutInArea(child, contentX, contentY, contentWidth, contentHeight, -1, HPos.CENTER, VPos.CENTER);
 170             }
 171         }
 172     }
 173     
 174     /**
 175      * Determines whether all mouse events should be automatically consumed.
 176      */
 177     protected final void consumeMouseEvents(boolean value) {
 178         if (value) {
 179             control.addEventHandler(MouseEvent.ANY, mouseEventConsumer);
 180         } else {
 181             control.removeEventHandler(MouseEvent.ANY, mouseEventConsumer);
 182         }
 183     }
 184     
 185     
 186     
 187     /***************************************************************************
 188      *                                                                         *
 189      * Public Layout-related API                                               *
 190      *                                                                         *
 191      **************************************************************************/
 192     
 193     /**
 194      * Computes the minimum allowable width of the Skin, based on the provided
 195      * height.
 196      *
 197      * @param height The height of the Skin, in case this value might dictate
 198      *      the minimum width.
 199      * @param topInset the pixel snapped top inset
 200      * @param rightInset the pixel snapped right inset
 201      * @param bottomInset the pixel snapped bottom inset
 202      * @param leftInset  the pixel snapped left inset
 203      * @return A double representing the minimum width of this Skin.
 204      */
 205     protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 206 
 207         double minX = 0;
 208         double maxX = 0;
 209         boolean firstManagedChild = true;
 210         for (int i = 0; i < children.size(); i++) {
 211             Node node = children.get(i);
 212             if (node.isManaged()) {
 213                 final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
 214                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 215                     minX = Math.min(minX, x);
 216                     maxX = Math.max(maxX, x + node.minWidth(-1));
 217                 } else {
 218                     minX = x;
 219                     maxX = x + node.minWidth(-1);
 220                     firstManagedChild = false;
 221                 }
 222             }
 223         }
 224         double minWidth = maxX - minX;
 225         return leftInset + minWidth + rightInset;
 226     }
 227 
 228     /**
 229      * Computes the minimum allowable height of the Skin, based on the provided
 230      * width.
 231      *
 232      * @param width The width of the Skin, in case this value might dictate
 233      *      the minimum height.
 234      * @param topInset the pixel snapped top inset
 235      * @param rightInset the pixel snapped right inset
 236      * @param bottomInset the pixel snapped bottom inset
 237      * @param leftInset  the pixel snapped left inset
 238      * @return A double representing the minimum height of this Skin.
 239      */
 240     protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 241 
 242         double minY = 0;
 243         double maxY = 0;
 244         boolean firstManagedChild = true;
 245         for (int i = 0; i < children.size(); i++) {
 246             Node node = children.get(i);
 247             if (node.isManaged()) {
 248                 final double y = node.getLayoutBounds().getMinY() + node.getLayoutY();
 249                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 250                     minY = Math.min(minY, y);
 251                     maxY = Math.max(maxY, y + node.minHeight(-1));
 252                 } else {
 253                     minY = y;
 254                     maxY = y + node.minHeight(-1);
 255                     firstManagedChild = false;
 256                 }
 257             }
 258         }
 259         double minHeight = maxY - minY;
 260         return topInset + minHeight + bottomInset;
 261     }
 262 
 263     /**
 264      * Computes the maximum allowable width of the Skin, based on the provided
 265      * height.
 266      *
 267      * @param height The height of the Skin, in case this value might dictate
 268      *      the maximum width.
 269      * @param topInset the pixel snapped top inset
 270      * @param rightInset the pixel snapped right inset
 271      * @param bottomInset the pixel snapped bottom inset
 272      * @param leftInset  the pixel snapped left inset
 273      * @return A double representing the maximum width of this Skin.
 274      */
 275     protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 276         return Double.MAX_VALUE;
 277     }
 278     
 279     /**
 280      * Computes the maximum allowable height of the Skin, based on the provided
 281      * width.
 282      *
 283      * @param width The width of the Skin, in case this value might dictate
 284      *      the maximum height.
 285      * @param topInset the pixel snapped top inset
 286      * @param rightInset the pixel snapped right inset
 287      * @param bottomInset the pixel snapped bottom inset
 288      * @param leftInset  the pixel snapped left inset
 289      * @return A double representing the maximum height of this Skin.
 290      */
 291     protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 292         return Double.MAX_VALUE;
 293     }
 294     
 295     // PENDING_DOC_REVIEW
 296     /**
 297      * Calculates the preferred width of this {@code SkinBase}. The default
 298      * implementation calculates this width as the width of the area occupied
 299      * by its managed children when they are positioned at their
 300      * current positions at their preferred widths.
 301      *
 302      * @param height the height that should be used if preferred width depends on it
 303      * @param topInset the pixel snapped top inset
 304      * @param rightInset the pixel snapped right inset
 305      * @param bottomInset the pixel snapped bottom inset
 306      * @param leftInset  the pixel snapped left inset
 307      * @return the calculated preferred width
 308      */
 309     protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 310 
 311         double minX = 0;
 312         double maxX = 0;
 313         boolean firstManagedChild = true;
 314         for (int i = 0; i < children.size(); i++) {
 315             Node node = children.get(i);
 316             if (node.isManaged()) {
 317                 final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
 318                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 319                     minX = Math.min(minX, x);
 320                     maxX = Math.max(maxX, x + node.prefWidth(-1));
 321                 } else {
 322                     minX = x;
 323                     maxX = x + node.prefWidth(-1);
 324                     firstManagedChild = false;
 325                 }
 326             }
 327         }
 328         return maxX - minX;
 329     }
 330     
 331     // PENDING_DOC_REVIEW
 332     /**
 333      * Calculates the preferred height of this {@code SkinBase}. The default
 334      * implementation calculates this height as the height of the area occupied
 335      * by its managed children when they are positioned at their current
 336      * positions at their preferred heights.
 337      *
 338      * @param width the width that should be used if preferred height depends on it
 339      * @param topInset the pixel snapped top inset
 340      * @param rightInset the pixel snapped right inset
 341      * @param bottomInset the pixel snapped bottom inset
 342      * @param leftInset  the pixel snapped left inset
 343      * @return the calculated preferred height
 344      */
 345     protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 346 
 347         double minY = 0;
 348         double maxY = 0;
 349         boolean firstManagedChild = true;
 350         for (int i = 0; i < children.size(); i++) {
 351             Node node = children.get(i);
 352             if (node.isManaged()) {
 353                 final double y = node.getLayoutBounds().getMinY() + node.getLayoutY();
 354                 if (!firstManagedChild) {  // branch prediction favors most often used condition
 355                     minY = Math.min(minY, y);
 356                     maxY = Math.max(maxY, y + node.prefHeight(-1));
 357                 } else {
 358                     minY = y;
 359                     maxY = y + node.prefHeight(-1);
 360                     firstManagedChild = false;
 361                 }
 362             }
 363         }
 364         return maxY - minY;
 365     }
 366     
 367     /**
 368      * Calculates the baseline offset based on the first managed child. If there
 369      * is no such child, returns {@link Node#getBaselineOffset()}.
 370      *
 371      * @param topInset the pixel snapped top inset
 372      * @param rightInset the pixel snapped right inset
 373      * @param bottomInset the pixel snapped bottom inset
 374      * @param leftInset  the pixel snapped left inset
 375      * @return baseline offset
 376      */
 377     protected double computeBaselineOffset(double topInset, double rightInset, double bottomInset, double leftInset) {
 378         int size = children.size();
 379         for (int i = 0; i < size; ++i) {
 380             Node child = children.get(i);
 381             if (child.isManaged()) {
 382                 double offset = child.getBaselineOffset();
 383                 if (offset == Node.BASELINE_OFFSET_SAME_AS_HEIGHT) {
 384                     continue;
 385                 }
 386                 return child.getLayoutBounds().getMinY() + child.getLayoutY() + offset;
 387             }
 388         }
 389         return Node.BASELINE_OFFSET_SAME_AS_HEIGHT;
 390     }
 391 
 392     
 393     /***************************************************************************
 394      *                                                                         *
 395      * (Mostly ugly) Skin -> Control forwarding API                            *
 396      *                                                                         *
 397      **************************************************************************/
 398 
 399     /**
 400      * Utility method to get the top inset which includes padding and border
 401      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 402      *
 403      * @return Rounded up insets top
 404      */
 405     protected double snappedTopInset() {
 406         return control.snappedTopInset();
 407     }
 408 
 409     /**
 410      * Utility method to get the bottom inset which includes padding and border
 411      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 412      *
 413      * @return Rounded up insets bottom
 414      */
 415     protected double snappedBottomInset() {
 416         return control.snappedBottomInset();
 417     }
 418 
 419     /**
 420      * Utility method to get the left inset which includes padding and border
 421      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 422      *
 423      * @return Rounded up insets left
 424      */
 425     protected double snappedLeftInset() {
 426         return control.snappedLeftInset();
 427     }
 428 
 429     /**
 430      * Utility method to get the right inset which includes padding and border
 431      * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
 432      *
 433      * @return Rounded up insets right
 434      */
 435     protected double snappedRightInset() {
 436         return control.snappedRightInset();
 437     }
 438 
 439     /**
 440      * If this region's snapToPixel property is true, returns a value rounded
 441      * to the nearest pixel, else returns the same value.
 442      * @param value the space value to be snapped
 443      * @return value rounded to nearest pixel
 444      */
 445     protected double snapSpace(double value) {
 446         return control.isSnapToPixel() ? Math.round(value) : value;
 447     }
 448     
 449     /**
 450      * If this region's snapToPixel property is true, returns a value ceiled
 451      * to the nearest pixel, else returns the same value.
 452      * @param value the size value to be snapped
 453      * @return value ceiled to nearest pixel
 454      */
 455     protected double snapSize(double value) {
 456         return control.isSnapToPixel() ? Math.ceil(value) : value;
 457     }
 458 
 459     /**
 460      * If this region's snapToPixel property is true, returns a value rounded
 461      * to the nearest pixel, else returns the same value.
 462      * @param value the position value to be snapped
 463      * @return value rounded to nearest pixel
 464      */
 465     protected double snapPosition(double value) {
 466         return control.isSnapToPixel() ? Math.round(value) : value;
 467     }
 468 
 469     /**
 470      * Utility method which positions the child within an area of this
 471      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 472      * with a baseline offset relative to that area.
 473      * <p>
 474      * This function does <i>not</i> resize the node and uses the node's layout bounds
 475      * width and height to determine how it should be positioned within the area.
 476      * <p>
 477      * If the vertical alignment is {@code VPos.BASELINE} then it
 478      * will position the node so that its own baseline aligns with the passed in
 479      * {@code baselineOffset}, otherwise the baseline parameter is ignored.
 480      * <p>
 481      * If {@code snapToPixel} is {@code true} for this skin, then the x/y position
 482      * values will be rounded to their nearest pixel boundaries.
 483      *
 484      * @param child the child being positioned within this skin
 485      * @param areaX the horizontal offset of the layout area relative to this skin
 486      * @param areaY the vertical offset of the layout area relative to this skin
 487      * @param areaWidth  the width of the layout area
 488      * @param areaHeight the height of the layout area
 489      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 490      * @param halignment the horizontal alignment for the child within the area
 491      * @param valignment the vertical alignment for the child within the area
 492      *
 493      */
 494     protected void positionInArea(Node child, double areaX, double areaY, 
 495             double areaWidth, double areaHeight, double areaBaselineOffset, 
 496             HPos halignment, VPos valignment) {
 497         positionInArea(child, areaX, areaY, areaWidth, areaHeight, 
 498                 areaBaselineOffset, Insets.EMPTY, halignment, valignment);
 499     }
 500 
 501     /**
 502      * Utility method which positions the child within an area of this
 503      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 504      * with a baseline offset relative to that area.
 505      * <p>
 506      * This function does <i>not</i> resize the node and uses the node's layout bounds
 507      * width and height to determine how it should be positioned within the area.
 508      * <p>
 509      * If the vertical alignment is {@code VPos.BASELINE} then it
 510      * will position the node so that its own baseline aligns with the passed in
 511      * {@code baselineOffset},  otherwise the baseline parameter is ignored.
 512      * <p>
 513      * If {@code snapToPixel} is {@code true} for this skin, then the x/y position
 514      * values will be rounded to their nearest pixel boundaries.
 515      * <p>
 516      * If {@code margin} is non-null, then that space will be allocated around the
 517      * child within the layout area.  margin may be null.
 518      *
 519      * @param child the child being positioned within this skin
 520      * @param areaX the horizontal offset of the layout area relative to this skin
 521      * @param areaY the vertical offset of the layout area relative to this skin
 522      * @param areaWidth  the width of the layout area
 523      * @param areaHeight the height of the layout area
 524      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 525      * @param margin the margin of space to be allocated around the child
 526      * @param halignment the horizontal alignment for the child within the area
 527      * @param valignment the vertical alignment for the child within the area
 528      *
 529      * @since JavaFX 8.0
 530      */
 531     protected void positionInArea(Node child, double areaX, double areaY,
 532             double areaWidth, double areaHeight, double areaBaselineOffset, 
 533             Insets margin, HPos halignment, VPos valignment) {
 534         Region.positionInArea(child, areaX, areaY, areaWidth, areaHeight, 
 535                 areaBaselineOffset, margin, halignment, valignment, 
 536                 control.isSnapToPixel());
 537     }
 538 
 539     /**
 540      * Utility method which lays out the child within an area of this
 541      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 542      * with a baseline offset relative to that area.
 543      * <p>
 544      * If the child is resizable, this method will resize it to fill the specified
 545      * area unless the node's maximum size prevents it.  If the node's maximum
 546      * size preference is less than the area size, the maximum size will be used.
 547      * If node's maximum is greater than the area size, then the node will be
 548      * resized to fit within the area, unless its minimum size prevents it.
 549      * <p>
 550      * If the child has a non-null contentBias, then this method will use it when
 551      * resizing the child.  If the contentBias is horizontal, it will set its width
 552      * first to the area's width (up to the child's max width limit) and then pass
 553      * that value to compute the child's height.  If child's contentBias is vertical,
 554      * then it will set its height to the area height (up to child's max height limit)
 555      * and pass that height to compute the child's width.  If the child's contentBias
 556      * is null, then it's width and height have no dependencies on each other.
 557      * <p>
 558      * If the child is not resizable (Shape, Group, etc) then it will only be
 559      * positioned and not resized.
 560      * <p>
 561      * If the child's resulting size differs from the area's size (either
 562      * because it was not resizable or it's sizing preferences prevented it), then
 563      * this function will align the node relative to the area using horizontal and
 564      * vertical alignment values.
 565      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 566      * with the area baseline offset parameter, otherwise the baseline parameter
 567      * is ignored.
 568      * <p>
 569      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 570      * values will be rounded to their nearest pixel boundaries and the
 571      * width/height values will be ceiled to the next pixel boundary.
 572      *
 573      * @param child the child being positioned within this skin
 574      * @param areaX the horizontal offset of the layout area relative to this skin
 575      * @param areaY the vertical offset of the layout area relative to this skin
 576      * @param areaWidth  the width of the layout area
 577      * @param areaHeight the height of the layout area
 578      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 579      * @param halignment the horizontal alignment for the child within the area
 580      * @param valignment the vertical alignment for the child within the area
 581      *
 582      */
 583     protected void layoutInArea(Node child, double areaX, double areaY,
 584                                double areaWidth, double areaHeight,
 585                                double areaBaselineOffset,
 586                                HPos halignment, VPos valignment) {
 587         layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, 
 588                 Insets.EMPTY, true, true, halignment, valignment);
 589     }
 590 
 591     /**
 592      * Utility method which lays out the child within an area of this
 593      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 594      * with a baseline offset relative to that area.
 595      * <p>
 596      * If the child is resizable, this method will resize it to fill the specified
 597      * area unless the node's maximum size prevents it.  If the node's maximum
 598      * size preference is less than the area size, the maximum size will be used.
 599      * If node's maximum is greater than the area size, then the node will be
 600      * resized to fit within the area, unless its minimum size prevents it.
 601      * <p>
 602      * If the child has a non-null contentBias, then this method will use it when
 603      * resizing the child.  If the contentBias is horizontal, it will set its width
 604      * first to the area's width (up to the child's max width limit) and then pass
 605      * that value to compute the child's height.  If child's contentBias is vertical,
 606      * then it will set its height to the area height (up to child's max height limit)
 607      * and pass that height to compute the child's width.  If the child's contentBias
 608      * is null, then it's width and height have no dependencies on each other.
 609      * <p>
 610      * If the child is not resizable (Shape, Group, etc) then it will only be
 611      * positioned and not resized.
 612      * <p>
 613      * If the child's resulting size differs from the area's size (either
 614      * because it was not resizable or it's sizing preferences prevented it), then
 615      * this function will align the node relative to the area using horizontal and
 616      * vertical alignment values.
 617      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 618      * with the area baseline offset parameter, otherwise the baseline parameter
 619      * is ignored.
 620      * <p>
 621      * If {@code margin} is non-null, then that space will be allocated around the
 622      * child within the layout area.  margin may be null.
 623      * <p>
 624      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 625      * values will be rounded to their nearest pixel boundaries and the
 626      * width/height values will be ceiled to the next pixel boundary.
 627      *
 628      * @param child the child being positioned within this skin
 629      * @param areaX the horizontal offset of the layout area relative to this skin
 630      * @param areaY the vertical offset of the layout area relative to this skin
 631      * @param areaWidth  the width of the layout area
 632      * @param areaHeight the height of the layout area
 633      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 634      * @param margin the margin of space to be allocated around the child
 635      * @param halignment the horizontal alignment for the child within the area
 636      * @param valignment the vertical alignment for the child within the area
 637      */
 638     protected void layoutInArea(Node child, double areaX, double areaY,
 639                                double areaWidth, double areaHeight,
 640                                double areaBaselineOffset,
 641                                Insets margin,
 642                                HPos halignment, VPos valignment) {
 643         layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset,
 644                 margin, true, true, halignment, valignment);
 645     }
 646 
 647     /**
 648      * Utility method which lays out the child within an area of this
 649      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 650      * with a baseline offset relative to that area.
 651      * <p>
 652      * If the child is resizable, this method will use {@code fillWidth} and {@code fillHeight}
 653      * to determine whether to resize it to fill the area or keep the child at its
 654      * preferred dimension.  If fillWidth/fillHeight are true, then this method
 655      * will only resize the child up to its max size limits.  If the node's maximum
 656      * size preference is less than the area size, the maximum size will be used.
 657      * If node's maximum is greater than the area size, then the node will be
 658      * resized to fit within the area, unless its minimum size prevents it.
 659      * <p>
 660      * If the child has a non-null contentBias, then this method will use it when
 661      * resizing the child.  If the contentBias is horizontal, it will set its width
 662      * first and then pass that value to compute the child's height.  If child's
 663      * contentBias is vertical, then it will set its height first
 664      * and pass that value to compute the child's width.  If the child's contentBias
 665      * is null, then it's width and height have no dependencies on each other.
 666      * <p>
 667      * If the child is not resizable (Shape, Group, etc) then it will only be
 668      * positioned and not resized.
 669      * <p>
 670      * If the child's resulting size differs from the area's size (either
 671      * because it was not resizable or it's sizing preferences prevented it), then
 672      * this function will align the node relative to the area using horizontal and
 673      * vertical alignment values.
 674      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 675      * with the area baseline offset parameter, otherwise the baseline parameter
 676      * is ignored.
 677      * <p>
 678      * If {@code margin} is non-null, then that space will be allocated around the
 679      * child within the layout area.  margin may be null.
 680      * <p>
 681      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 682      * values will be rounded to their nearest pixel boundaries and the
 683      * width/height values will be ceiled to the next pixel boundary.
 684      *
 685      * @param child the child being positioned within this skin
 686      * @param areaX the horizontal offset of the layout area relative to this skin
 687      * @param areaY the vertical offset of the layout area relative to this skin
 688      * @param areaWidth  the width of the layout area
 689      * @param areaHeight the height of the layout area
 690      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 691      * @param margin the margin of space to be allocated around the child
 692      * @param fillWidth whether or not the child should be resized to fill the area width or kept to its preferred width
 693      * @param fillHeight whether or not the child should e resized to fill the area height or kept to its preferred height
 694      * @param halignment the horizontal alignment for the child within the area
 695      * @param valignment the vertical alignment for the child within the area
 696      */
 697     protected void layoutInArea(Node child, double areaX, double areaY,
 698                                double areaWidth, double areaHeight,
 699                                double areaBaselineOffset,
 700                                Insets margin, boolean fillWidth, boolean fillHeight,
 701                                HPos halignment, VPos valignment) {
 702         Region.layoutInArea(child, areaX, areaY, areaWidth, areaHeight, 
 703                 areaBaselineOffset, margin, fillWidth, fillHeight, halignment, 
 704                 valignment, control.isSnapToPixel());
 705     }
 706     
 707     
 708     
 709     /***************************************************************************
 710      *                                                                         *
 711      * Private Implementation                                                  *
 712      *                                                                         *
 713      **************************************************************************/     
 714     
 715     
 716     
 717      /**************************************************************************
 718       *                                                                        *
 719       * Specialization of CSS handling code                                    *
 720       *                                                                        *
 721      **************************************************************************/
 722 
 723     private static class StyleableProperties {
 724 
 725         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 726 
 727         static {
 728             STYLEABLES = Collections.unmodifiableList(Control.getClassCssMetaData());
 729         }
 730     }
 731 
 732     /** 
 733      * @return The CssMetaData associated with this class, which may include the
 734      * CssMetaData of its super classes.
 735      */    public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 736         return SkinBase.StyleableProperties.STYLEABLES;
 737     }
 738 
 739     /**
 740      * This method should delegate to {@link Node#getClassCssMetaData()} so that
 741      * a Node's CssMetaData can be accessed without the need for reflection.
 742      * @return The CssMetaData associated with this node, which may include the
 743      * CssMetaData of its super classes.
 744      */
 745     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
 746         return getClassCssMetaData();
 747     }
 748     
 749     /** @see Node#pseudoClassStateChanged */
 750     public final void pseudoClassStateChanged(PseudoClass pseudoClass, boolean active) {
 751         Control ctl = getSkinnable();
 752         if (ctl != null) {
 753             ctl.pseudoClassStateChanged(pseudoClass, active);
 754         }
 755     }
 756 
 757 
 758     /***************************************************************************
 759      *                                                                         *
 760      * Accessibility handling                                                  *
 761      *                                                                         *
 762      **************************************************************************/
 763 
 764     /** @see Node#queryAccessibleAttribute */
 765     protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 766         return null;
 767     }
 768 
 769     /** @see Node#executeAccessibleAction */
 770     protected void executeAccessibleAction(AccessibleAction action, Object... parameters) {
 771     }
 772 
 773     /***************************************************************************
 774      *                                                                         *
 775      * Testing-only API                                                        *
 776      *                                                                         *
 777      **************************************************************************/
 778 
 779 }