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