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 }