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 true, returns a value rounded
 490      * to the nearest pixel, else returns the same value.
 491      * @param value the space value to be snapped
 492      * @return value rounded to nearest pixel
 493      */
 494     protected double snapSpace(double value) {
 495         return control.isSnapToPixel() ? Math.round(value) : value;
 496     }
 497 
 498     /**
 499      * If this region's snapToPixel property is true, returns a value ceiled
 500      * to the nearest pixel, else returns the same value.
 501      * @param value the size value to be snapped
 502      * @return value ceiled to nearest pixel
 503      */
 504     protected double snapSize(double value) {
 505         return control.isSnapToPixel() ? Math.ceil(value) : value;
 506     }
 507 
 508     /**
 509      * If this region's snapToPixel property is true, returns a value rounded
 510      * to the nearest pixel, else returns the same value.
 511      * @param value the position value to be snapped
 512      * @return value rounded to nearest pixel
 513      */
 514     protected double snapPosition(double value) {
 515         return control.isSnapToPixel() ? Math.round(value) : value;
 516     }
 517 
 518     /**
 519      * Utility method which positions the child within an area of this
 520      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 521      * with a baseline offset relative to that area.
 522      * <p>
 523      * This function does <i>not</i> resize the node and uses the node's layout bounds
 524      * width and height to determine how it should be positioned within the area.
 525      * <p>
 526      * If the vertical alignment is {@code VPos.BASELINE} then it
 527      * will position the node so that its own baseline aligns with the passed in
 528      * {@code baselineOffset}, otherwise the baseline parameter is ignored.
 529      * <p>
 530      * If {@code snapToPixel} is {@code true} for this skin, then the x/y position
 531      * values will be rounded to their nearest pixel boundaries.
 532      *
 533      * @param child the child being positioned within this skin
 534      * @param areaX the horizontal offset of the layout area relative to this skin
 535      * @param areaY the vertical offset of the layout area relative to this skin
 536      * @param areaWidth  the width of the layout area
 537      * @param areaHeight the height of the layout area
 538      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 539      * @param halignment the horizontal alignment for the child within the area
 540      * @param valignment the vertical alignment for the child within the area
 541      *
 542      */
 543     protected void positionInArea(Node child, double areaX, double areaY,
 544             double areaWidth, double areaHeight, double areaBaselineOffset,
 545             HPos halignment, VPos valignment) {
 546         positionInArea(child, areaX, areaY, areaWidth, areaHeight,
 547                 areaBaselineOffset, Insets.EMPTY, halignment, valignment);
 548     }
 549 
 550     /**
 551      * Utility method which positions the child within an area of this
 552      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 553      * with a baseline offset relative to that area.
 554      * <p>
 555      * This function does <i>not</i> resize the node and uses the node's layout bounds
 556      * width and height to determine how it should be positioned within the area.
 557      * <p>
 558      * If the vertical alignment is {@code VPos.BASELINE} then it
 559      * will position the node so that its own baseline aligns with the passed in
 560      * {@code baselineOffset},  otherwise the baseline parameter is ignored.
 561      * <p>
 562      * If {@code snapToPixel} is {@code true} for this skin, then the x/y position
 563      * values will be rounded to their nearest pixel boundaries.
 564      * <p>
 565      * If {@code margin} is non-null, then that space will be allocated around the
 566      * child within the layout area.  margin may be null.
 567      *
 568      * @param child the child being positioned within this skin
 569      * @param areaX the horizontal offset of the layout area relative to this skin
 570      * @param areaY the vertical offset of the layout area relative to this skin
 571      * @param areaWidth  the width of the layout area
 572      * @param areaHeight the height of the layout area
 573      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 574      * @param margin the margin of space to be allocated around the child
 575      * @param halignment the horizontal alignment for the child within the area
 576      * @param valignment the vertical alignment for the child within the area
 577      *
 578      * @since JavaFX 8.0
 579      */
 580     protected void positionInArea(Node child, double areaX, double areaY,
 581             double areaWidth, double areaHeight, double areaBaselineOffset,
 582             Insets margin, HPos halignment, VPos valignment) {
 583         Region.positionInArea(child, areaX, areaY, areaWidth, areaHeight,
 584                 areaBaselineOffset, margin, halignment, valignment,
 585                 control.isSnapToPixel());
 586     }
 587 
 588     /**
 589      * Utility method which lays out the child within an area of this
 590      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 591      * with a baseline offset relative to that area.
 592      * <p>
 593      * If the child is resizable, this method will resize it to fill the specified
 594      * area unless the node's maximum size prevents it.  If the node's maximum
 595      * size preference is less than the area size, the maximum size will be used.
 596      * If node's maximum is greater than the area size, then the node will be
 597      * resized to fit within the area, unless its minimum size prevents it.
 598      * <p>
 599      * If the child has a non-null contentBias, then this method will use it when
 600      * resizing the child.  If the contentBias is horizontal, it will set its width
 601      * first to the area's width (up to the child's max width limit) and then pass
 602      * that value to compute the child's height.  If child's contentBias is vertical,
 603      * then it will set its height to the area height (up to child's max height limit)
 604      * and pass that height to compute the child's width.  If the child's contentBias
 605      * is null, then it's width and height have no dependencies on each other.
 606      * <p>
 607      * If the child is not resizable (Shape, Group, etc) then it will only be
 608      * positioned and not resized.
 609      * <p>
 610      * If the child's resulting size differs from the area's size (either
 611      * because it was not resizable or it's sizing preferences prevented it), then
 612      * this function will align the node relative to the area using horizontal and
 613      * vertical alignment values.
 614      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 615      * with the area baseline offset parameter, otherwise the baseline parameter
 616      * is ignored.
 617      * <p>
 618      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 619      * values will be rounded to their nearest pixel boundaries and the
 620      * width/height values will be ceiled to the next pixel boundary.
 621      *
 622      * @param child the child being positioned within this skin
 623      * @param areaX the horizontal offset of the layout area relative to this skin
 624      * @param areaY the vertical offset of the layout area relative to this skin
 625      * @param areaWidth  the width of the layout area
 626      * @param areaHeight the height of the layout area
 627      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 628      * @param halignment the horizontal alignment for the child within the area
 629      * @param valignment the vertical alignment for the child within the area
 630      *
 631      */
 632     protected void layoutInArea(Node child, double areaX, double areaY,
 633                                double areaWidth, double areaHeight,
 634                                double areaBaselineOffset,
 635                                HPos halignment, VPos valignment) {
 636         layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset,
 637                 Insets.EMPTY, true, true, halignment, valignment);
 638     }
 639 
 640     /**
 641      * Utility method which lays out the child within an area of this
 642      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 643      * with a baseline offset relative to that area.
 644      * <p>
 645      * If the child is resizable, this method will resize it to fill the specified
 646      * area unless the node's maximum size prevents it.  If the node's maximum
 647      * size preference is less than the area size, the maximum size will be used.
 648      * If node's maximum is greater than the area size, then the node will be
 649      * resized to fit within the area, unless its minimum size prevents it.
 650      * <p>
 651      * If the child has a non-null contentBias, then this method will use it when
 652      * resizing the child.  If the contentBias is horizontal, it will set its width
 653      * first to the area's width (up to the child's max width limit) and then pass
 654      * that value to compute the child's height.  If child's contentBias is vertical,
 655      * then it will set its height to the area height (up to child's max height limit)
 656      * and pass that height to compute the child's width.  If the child's contentBias
 657      * is null, then it's width and height have no dependencies on each other.
 658      * <p>
 659      * If the child is not resizable (Shape, Group, etc) then it will only be
 660      * positioned and not resized.
 661      * <p>
 662      * If the child's resulting size differs from the area's size (either
 663      * because it was not resizable or it's sizing preferences prevented it), then
 664      * this function will align the node relative to the area using horizontal and
 665      * vertical alignment values.
 666      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 667      * with the area baseline offset parameter, otherwise the baseline parameter
 668      * is ignored.
 669      * <p>
 670      * If {@code margin} is non-null, then that space will be allocated around the
 671      * child within the layout area.  margin may be null.
 672      * <p>
 673      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 674      * values will be rounded to their nearest pixel boundaries and the
 675      * width/height values will be ceiled to the next pixel boundary.
 676      *
 677      * @param child the child being positioned within this skin
 678      * @param areaX the horizontal offset of the layout area relative to this skin
 679      * @param areaY the vertical offset of the layout area relative to this skin
 680      * @param areaWidth  the width of the layout area
 681      * @param areaHeight the height of the layout area
 682      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 683      * @param margin the margin of space to be allocated around the child
 684      * @param halignment the horizontal alignment for the child within the area
 685      * @param valignment the vertical alignment for the child within the area
 686      */
 687     protected void layoutInArea(Node child, double areaX, double areaY,
 688                                double areaWidth, double areaHeight,
 689                                double areaBaselineOffset,
 690                                Insets margin,
 691                                HPos halignment, VPos valignment) {
 692         layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset,
 693                 margin, true, true, halignment, valignment);
 694     }
 695 
 696     /**
 697      * Utility method which lays out the child within an area of this
 698      * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
 699      * with a baseline offset relative to that area.
 700      * <p>
 701      * If the child is resizable, this method will use {@code fillWidth} and {@code fillHeight}
 702      * to determine whether to resize it to fill the area or keep the child at its
 703      * preferred dimension.  If fillWidth/fillHeight are true, then this method
 704      * will only resize the child up to its max size limits.  If the node's maximum
 705      * size preference is less than the area size, the maximum size will be used.
 706      * If node's maximum is greater than the area size, then the node will be
 707      * resized to fit within the area, unless its minimum size prevents it.
 708      * <p>
 709      * If the child has a non-null contentBias, then this method will use it when
 710      * resizing the child.  If the contentBias is horizontal, it will set its width
 711      * first and then pass that value to compute the child's height.  If child's
 712      * contentBias is vertical, then it will set its height first
 713      * and pass that value to compute the child's width.  If the child's contentBias
 714      * is null, then it's width and height have no dependencies on each other.
 715      * <p>
 716      * If the child is not resizable (Shape, Group, etc) then it will only be
 717      * positioned and not resized.
 718      * <p>
 719      * If the child's resulting size differs from the area's size (either
 720      * because it was not resizable or it's sizing preferences prevented it), then
 721      * this function will align the node relative to the area using horizontal and
 722      * vertical alignment values.
 723      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
 724      * with the area baseline offset parameter, otherwise the baseline parameter
 725      * is ignored.
 726      * <p>
 727      * If {@code margin} is non-null, then that space will be allocated around the
 728      * child within the layout area.  margin may be null.
 729      * <p>
 730      * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y
 731      * values will be rounded to their nearest pixel boundaries and the
 732      * width/height values will be ceiled to the next pixel boundary.
 733      *
 734      * @param child the child being positioned within this skin
 735      * @param areaX the horizontal offset of the layout area relative to this skin
 736      * @param areaY the vertical offset of the layout area relative to this skin
 737      * @param areaWidth  the width of the layout area
 738      * @param areaHeight the height of the layout area
 739      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
 740      * @param margin the margin of space to be allocated around the child
 741      * @param fillWidth whether or not the child should be resized to fill the area width or kept to its preferred width
 742      * @param fillHeight whether or not the child should e resized to fill the area height or kept to its preferred height
 743      * @param halignment the horizontal alignment for the child within the area
 744      * @param valignment the vertical alignment for the child within the area
 745      */
 746     protected void layoutInArea(Node child, double areaX, double areaY,
 747                                double areaWidth, double areaHeight,
 748                                double areaBaselineOffset,
 749                                Insets margin, boolean fillWidth, boolean fillHeight,
 750                                HPos halignment, VPos valignment) {
 751         Region.layoutInArea(child, areaX, areaY, areaWidth, areaHeight,
 752                 areaBaselineOffset, margin, fillWidth, fillHeight, halignment,
 753                 valignment, control.isSnapToPixel());
 754     }
 755 
 756 
 757 
 758     /***************************************************************************
 759      *                                                                         *
 760      * Private Implementation                                                  *
 761      *                                                                         *
 762      **************************************************************************/
 763 
 764 
 765 
 766      /**************************************************************************
 767       *                                                                        *
 768       * Specialization of CSS handling code                                    *
 769       *                                                                        *
 770      **************************************************************************/
 771 
 772     private static class StyleableProperties {
 773         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 774 
 775         static {
 776             STYLEABLES = Collections.unmodifiableList(Control.getClassCssMetaData());
 777         }
 778     }
 779 
 780     /**
 781      * Returns the CssMetaData associated with this class, which may include the
 782      * CssMetaData of its super classes.
 783      */
 784     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 785         return SkinBase.StyleableProperties.STYLEABLES;
 786     }
 787 
 788     /**
 789      * This method should delegate to {@link Node#getClassCssMetaData()} so that
 790      * a Node's CssMetaData can be accessed without the need for reflection.
 791      * @return The CssMetaData associated with this node, which may include the
 792      * CssMetaData of its super classes.
 793      */
 794     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
 795         return getClassCssMetaData();
 796     }
 797 
 798     /**
 799      * Used to specify that a pseudo-class of this Node has changed. If the
 800      * pseudo-class is used in a CSS selector that matches this Node, CSS will
 801      * be reapplied. Typically, this method is called from the {@code invalidated}
 802      * method of a property that is used as a pseudo-class. For example:
 803      * <code><pre>
 804      *
 805      *     private static final PseudoClass MY_PSEUDO_CLASS_STATE = PseudoClass.getPseudoClass("my-state");
 806      *
 807      *     BooleanProperty myPseudoClassState = new BooleanPropertyBase(false) {
 808      *
 809      *           {@literal @}Override public void invalidated() {
 810      *                pseudoClassStateChanged(MY_PSEUDO_CLASS_STATE, get());
 811      *           }
 812      *
 813      *           {@literal @}Override public Object getBean() {
 814      *               return MyControl.this;
 815      *           }
 816      *
 817      *           {@literal @}Override public String getName() {
 818      *               return "myPseudoClassState";
 819      *           }
 820      *       };
 821      * </pre><code>
 822      *
 823      * @see Node#pseudoClassStateChanged
 824      * @param pseudoClass the pseudo-class that has changed state
 825      * @param active whether or not the state is active
 826      * @since JavaFX 8.0
 827      */
 828     public final void pseudoClassStateChanged(PseudoClass pseudoClass, boolean active) {
 829         Control ctl = getSkinnable();
 830         if (ctl != null) {
 831             ctl.pseudoClassStateChanged(pseudoClass, active);
 832         }
 833     }
 834 
 835 
 836     /***************************************************************************
 837      *                                                                         *
 838      * Accessibility handling                                                  *
 839      *                                                                         *
 840      **************************************************************************/
 841 
 842     /**
 843      * This method is called by the assistive technology to request
 844      * the value for an attribute.
 845      * <p>
 846      * This method is commonly overridden by subclasses to implement
 847      * attributes that are required for a specific role.<br>
 848      * If a particular attribute is not handled, the super class implementation
 849      * must be called.
 850      * </p>
 851      *
 852      * @param attribute the requested attribute
 853      * @param parameters optional list of parameters
 854      * @return the value for the requested attribute
 855      *
 856      * @see AccessibleAttribute
 857      * @see Node#queryAccessibleAttribute
 858      *
 859      * @since JavaFX 8u40
 860      */
 861     protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 862         return null;
 863     }
 864 
 865     /**
 866      * This method is called by the assistive technology to request the action
 867      * indicated by the argument should be executed.
 868      * <p>
 869      * This method is commonly overridden by subclasses to implement
 870      * action that are required for a specific role.<br>
 871      * If a particular action is not handled, the super class implementation
 872      * must be called.
 873      * </p>
 874      *
 875      * @param action the action to execute
 876      * @param parameters optional list of parameters
 877      *
 878      * @see AccessibleAction
 879      * @see Node#executeAccessibleAction
 880      *
 881      * @since JavaFX 8u40
 882      */
 883     protected void executeAccessibleAction(AccessibleAction action, Object... parameters) {
 884     }
 885 
 886     /***************************************************************************
 887      *                                                                         *
 888      * Testing-only API                                                        *
 889      *                                                                         *
 890      **************************************************************************/
 891 
 892 }