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