1 /* 2 * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.scene.control; 27 28 import com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler; 29 import javafx.beans.value.ObservableValue; 30 import javafx.css.CssMetaData; 31 import javafx.css.PseudoClass; 32 import java.util.Collections; 33 import java.util.List; 34 import java.util.function.Consumer; 35 36 import javafx.collections.ObservableList; 37 import javafx.css.Styleable; 38 import javafx.event.EventHandler; 39 import javafx.geometry.HPos; 40 import javafx.geometry.Insets; 41 import javafx.geometry.VPos; 42 import javafx.scene.AccessibleAction; 43 import javafx.scene.AccessibleAttribute; 44 import javafx.scene.Node; 45 import javafx.scene.input.MouseEvent; 46 import javafx.scene.layout.Region; 47 48 /** 49 * Base implementation class for defining the visual representation of user 50 * interface controls by defining a scene graph of nodes to represent the 51 * {@link Skin skin}. 52 * A user interface control is abstracted behind the {@link Skinnable} interface. 53 * 54 * @since JavaFX 8.0 55 */ 56 public abstract class SkinBase<C extends Control> implements Skin<C> { 57 58 /*************************************************************************** 59 * * 60 * Private fields * 61 * * 62 **************************************************************************/ 63 64 /** 65 * The {@code Control} that is referencing this Skin. There is a 66 * one-to-one relationship between a {@code Skin} and a {@code Control}. 67 * When a {@code Skin} is set on a {@code Control}, this variable is 68 * automatically updated. 69 */ 70 private C control; 71 72 /** 73 * A local field that directly refers to the children list inside the Control. 74 */ 75 private ObservableList<Node> children; 76 77 /** 78 * This is part of the workaround introduced during delomboking. We probably will 79 * want to adjust the way listeners are added rather than continuing to use this 80 * map (although it doesn't really do much harm). 81 */ 82 private LambdaMultiplePropertyChangeListenerHandler lambdaChangeListenerHandler; 83 84 85 86 /*************************************************************************** 87 * * 88 * Event Handlers / Listeners * 89 * * 90 **************************************************************************/ 91 92 /** 93 * Mouse handler used for consuming all mouse events (preventing them 94 * from bubbling up to parent) 95 */ 96 private static final EventHandler<MouseEvent> mouseEventConsumer = event -> { 97 /* 98 ** we used to consume mouse wheel rotations here, 99 ** be we've switched to ScrollEvents, and only consume those which we use. 100 ** See RT-13995 & RT-14480 101 */ 102 event.consume(); 103 }; 104 105 106 107 /*************************************************************************** 108 * * 109 * Constructor * 110 * * 111 **************************************************************************/ 112 113 /** 114 * Constructor for all SkinBase instances. 115 * 116 * @param control The control for which this Skin should attach to. 117 */ 118 protected SkinBase(final C control) { 119 if (control == null) { 120 throw new IllegalArgumentException("Cannot pass null for control"); 121 } 122 123 // Update the control and behavior 124 this.control = control; 125 this.children = control.getControlChildren(); 126 127 // Default behavior for controls is to consume all mouse events 128 consumeMouseEvents(true); 129 } 130 131 132 133 /*************************************************************************** 134 * * 135 * Public API (from Skin) * 136 * * 137 **************************************************************************/ 138 139 /** {@inheritDoc} */ 140 @Override public final C getSkinnable() { 141 return control; 142 } 143 144 /** {@inheritDoc} */ 145 @Override public final Node getNode() { 146 return control; 147 } 148 149 /** {@inheritDoc} */ 150 @Override public void dispose() { 151 // control.removeEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler); 152 153 // unhook listeners 154 if (lambdaChangeListenerHandler != null) { 155 lambdaChangeListenerHandler.dispose(); 156 } 157 158 this.control = null; 159 } 160 161 162 163 /*************************************************************************** 164 * * 165 * Public API * 166 * * 167 **************************************************************************/ 168 169 /** 170 * Returns the children of the skin. 171 */ 172 public final ObservableList<Node> getChildren() { 173 return children; 174 } 175 176 /** 177 * Called during the layout pass of the scenegraph. 178 */ 179 protected void layoutChildren(final double contentX, final double contentY, 180 final double contentWidth, final double contentHeight) { 181 // By default simply sizes all managed children to fit within the space provided 182 for (int i=0, max=children.size(); i<max; i++) { 183 Node child = children.get(i); 184 if (child.isManaged()) { 185 layoutInArea(child, contentX, contentY, contentWidth, contentHeight, -1, HPos.CENTER, VPos.CENTER); 186 } 187 } 188 } 189 190 /** 191 * Determines whether all mouse events should be automatically consumed. 192 */ 193 protected final void consumeMouseEvents(boolean value) { 194 if (value) { 195 control.addEventHandler(MouseEvent.ANY, mouseEventConsumer); 196 } else { 197 control.removeEventHandler(MouseEvent.ANY, mouseEventConsumer); 198 } 199 } 200 201 202 /** 203 * Subclasses can invoke this method to register that they want to listen to 204 * property change events for the given property. Registered {@link Consumer} instances 205 * will be executed in the order in which they are registered. 206 */ 207 protected final void registerChangeListener(ObservableValue<?> property, Consumer<ObservableValue<?>> consumer) { 208 if (lambdaChangeListenerHandler == null) { 209 lambdaChangeListenerHandler = new LambdaMultiplePropertyChangeListenerHandler(); 210 } 211 lambdaChangeListenerHandler.registerChangeListener(property, consumer); 212 } 213 214 /** 215 * Unregisters all change listeners that have been registered using {@link #registerChangeListener(ObservableValue, Consumer)} 216 * for the given property. The end result is that the given property is no longer observed by any of the change 217 * listeners, but it may still have additional listeners registered on it through means outside of 218 * {@link #registerChangeListener(ObservableValue, Consumer)}. 219 * 220 * @param property The property for which all listeners should be removed. 221 * @return A single chained {@link Consumer} consisting of all {@link Consumer consumers} registered through 222 * {@link #registerChangeListener(ObservableValue, Consumer)}. If no consumers have been registered on this 223 * property, null will be returned. 224 * @since 9 225 */ 226 protected final Consumer<ObservableValue<?>> unregisterChangeListeners(ObservableValue<?> property) { 227 if (lambdaChangeListenerHandler == null) { 228 return null; 229 } 230 return lambdaChangeListenerHandler.unregisterChangeListeners(property); 231 } 232 233 234 235 236 /*************************************************************************** 237 * * 238 * Public Layout-related API * 239 * * 240 **************************************************************************/ 241 242 /** 243 * Computes the minimum allowable width of the Skin, based on the provided 244 * height. 245 * 246 * @param height The height of the Skin, in case this value might dictate 247 * the minimum width. 248 * @param topInset the pixel snapped top inset 249 * @param rightInset the pixel snapped right inset 250 * @param bottomInset the pixel snapped bottom inset 251 * @param leftInset the pixel snapped left inset 252 * @return A double representing the minimum width of this Skin. 253 */ 254 protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { 255 256 double minX = 0; 257 double maxX = 0; 258 boolean firstManagedChild = true; 259 for (int i = 0; i < children.size(); i++) { 260 Node node = children.get(i); 261 if (node.isManaged()) { 262 final double x = node.getLayoutBounds().getMinX() + node.getLayoutX(); 263 if (!firstManagedChild) { // branch prediction favors most often used condition 264 minX = Math.min(minX, x); 265 maxX = Math.max(maxX, x + node.minWidth(-1)); 266 } else { 267 minX = x; 268 maxX = x + node.minWidth(-1); 269 firstManagedChild = false; 270 } 271 } 272 } 273 double minWidth = maxX - minX; 274 return leftInset + minWidth + rightInset; 275 } 276 277 /** 278 * Computes the minimum allowable height of the Skin, based on the provided 279 * width. 280 * 281 * @param width The width of the Skin, in case this value might dictate 282 * the minimum height. 283 * @param topInset the pixel snapped top inset 284 * @param rightInset the pixel snapped right inset 285 * @param bottomInset the pixel snapped bottom inset 286 * @param leftInset the pixel snapped left inset 287 * @return A double representing the minimum height of this Skin. 288 */ 289 protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { 290 291 double minY = 0; 292 double maxY = 0; 293 boolean firstManagedChild = true; 294 for (int i = 0; i < children.size(); i++) { 295 Node node = children.get(i); 296 if (node.isManaged()) { 297 final double y = node.getLayoutBounds().getMinY() + node.getLayoutY(); 298 if (!firstManagedChild) { // branch prediction favors most often used condition 299 minY = Math.min(minY, y); 300 maxY = Math.max(maxY, y + node.minHeight(-1)); 301 } else { 302 minY = y; 303 maxY = y + node.minHeight(-1); 304 firstManagedChild = false; 305 } 306 } 307 } 308 double minHeight = maxY - minY; 309 return topInset + minHeight + bottomInset; 310 } 311 312 /** 313 * Computes the maximum allowable width of the Skin, based on the provided 314 * height. 315 * 316 * @param height The height of the Skin, in case this value might dictate 317 * the maximum width. 318 * @param topInset the pixel snapped top inset 319 * @param rightInset the pixel snapped right inset 320 * @param bottomInset the pixel snapped bottom inset 321 * @param leftInset the pixel snapped left inset 322 * @return A double representing the maximum width of this Skin. 323 */ 324 protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { 325 return Double.MAX_VALUE; 326 } 327 328 /** 329 * Computes the maximum allowable height of the Skin, based on the provided 330 * width. 331 * 332 * @param width The width of the Skin, in case this value might dictate 333 * the maximum height. 334 * @param topInset the pixel snapped top inset 335 * @param rightInset the pixel snapped right inset 336 * @param bottomInset the pixel snapped bottom inset 337 * @param leftInset the pixel snapped left inset 338 * @return A double representing the maximum height of this Skin. 339 */ 340 protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { 341 return Double.MAX_VALUE; 342 } 343 344 // PENDING_DOC_REVIEW 345 /** 346 * Calculates the preferred width of this {@code SkinBase}. The default 347 * implementation calculates this width as the width of the area occupied 348 * by its managed children when they are positioned at their 349 * current positions at their preferred widths. 350 * 351 * @param height the height that should be used if preferred width depends on it 352 * @param topInset the pixel snapped top inset 353 * @param rightInset the pixel snapped right inset 354 * @param bottomInset the pixel snapped bottom inset 355 * @param leftInset the pixel snapped left inset 356 * @return the calculated preferred width 357 */ 358 protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { 359 360 double minX = 0; 361 double maxX = 0; 362 boolean firstManagedChild = true; 363 for (int i = 0; i < children.size(); i++) { 364 Node node = children.get(i); 365 if (node.isManaged()) { 366 final double x = node.getLayoutBounds().getMinX() + node.getLayoutX(); 367 if (!firstManagedChild) { // branch prediction favors most often used condition 368 minX = Math.min(minX, x); 369 maxX = Math.max(maxX, x + node.prefWidth(-1)); 370 } else { 371 minX = x; 372 maxX = x + node.prefWidth(-1); 373 firstManagedChild = false; 374 } 375 } 376 } 377 return maxX - minX; 378 } 379 380 // PENDING_DOC_REVIEW 381 /** 382 * Calculates the preferred height of this {@code SkinBase}. The default 383 * implementation calculates this height as the height of the area occupied 384 * by its managed children when they are positioned at their current 385 * positions at their preferred heights. 386 * 387 * @param width the width that should be used if preferred height depends on it 388 * @param topInset the pixel snapped top inset 389 * @param rightInset the pixel snapped right inset 390 * @param bottomInset the pixel snapped bottom inset 391 * @param leftInset the pixel snapped left inset 392 * @return the calculated preferred height 393 */ 394 protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { 395 396 double minY = 0; 397 double maxY = 0; 398 boolean firstManagedChild = true; 399 for (int i = 0; i < children.size(); i++) { 400 Node node = children.get(i); 401 if (node.isManaged()) { 402 final double y = node.getLayoutBounds().getMinY() + node.getLayoutY(); 403 if (!firstManagedChild) { // branch prediction favors most often used condition 404 minY = Math.min(minY, y); 405 maxY = Math.max(maxY, y + node.prefHeight(-1)); 406 } else { 407 minY = y; 408 maxY = y + node.prefHeight(-1); 409 firstManagedChild = false; 410 } 411 } 412 } 413 return maxY - minY; 414 } 415 416 /** 417 * Calculates the baseline offset based on the first managed child. If there 418 * is no such child, returns {@link Node#getBaselineOffset()}. 419 * 420 * @param topInset the pixel snapped top inset 421 * @param rightInset the pixel snapped right inset 422 * @param bottomInset the pixel snapped bottom inset 423 * @param leftInset the pixel snapped left inset 424 * @return baseline offset 425 */ 426 protected double computeBaselineOffset(double topInset, double rightInset, double bottomInset, double leftInset) { 427 int size = children.size(); 428 for (int i = 0; i < size; ++i) { 429 Node child = children.get(i); 430 if (child.isManaged()) { 431 double offset = child.getBaselineOffset(); 432 if (offset == Node.BASELINE_OFFSET_SAME_AS_HEIGHT) { 433 continue; 434 } 435 return child.getLayoutBounds().getMinY() + child.getLayoutY() + offset; 436 } 437 } 438 return Node.BASELINE_OFFSET_SAME_AS_HEIGHT; 439 } 440 441 442 /*************************************************************************** 443 * * 444 * (Mostly ugly) Skin -> Control forwarding API * 445 * * 446 **************************************************************************/ 447 448 /** 449 * Utility method to get the top inset which includes padding and border 450 * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true. 451 * 452 * @return Rounded up insets top 453 */ 454 protected double snappedTopInset() { 455 return control.snappedTopInset(); 456 } 457 458 /** 459 * Utility method to get the bottom inset which includes padding and border 460 * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true. 461 * 462 * @return Rounded up insets bottom 463 */ 464 protected double snappedBottomInset() { 465 return control.snappedBottomInset(); 466 } 467 468 /** 469 * Utility method to get the left inset which includes padding and border 470 * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true. 471 * 472 * @return Rounded up insets left 473 */ 474 protected double snappedLeftInset() { 475 return control.snappedLeftInset(); 476 } 477 478 /** 479 * Utility method to get the right inset which includes padding and border 480 * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true. 481 * 482 * @return Rounded up insets right 483 */ 484 protected double snappedRightInset() { 485 return control.snappedRightInset(); 486 } 487 488 /** 489 * If this region's snapToPixel property is false, this method returns the 490 * same value, else it tries to return a value rounded to the nearest 491 * pixel, but since there is no indication if the value is a vertical 492 * or horizontal measurement then it may be snapped to the wrong pixel 493 * size metric on screens with different horizontal and vertical scales. 494 * @param value the space value to be snapped 495 * @return value rounded to nearest pixel 496 * @deprecated replaced by {@code snapSpaceX()} and {@code snapSpaceY()} 497 */ 498 protected double snapSpace(double value) { 499 return control.snapSpaceX(value); 500 } 501 502 /** 503 * If this region's snapToPixel property is true, returns a value rounded 504 * to the nearest pixel in the horizontal direction, else returns the 505 * same value. 506 * @param value the space value to be snapped 507 * @return value rounded to nearest pixel 508 * @since 9 509 */ 510 protected double snapSpaceX(double value) { 511 return control.snapSpaceX(value); 512 } 513 514 /** 515 * If this region's snapToPixel property is true, returns a value rounded 516 * to the nearest pixel in the vertical direction, else returns the 517 * same value. 518 * @param value the space value to be snapped 519 * @return value rounded to nearest pixel 520 * @since 9 521 */ 522 protected double snapSpaceY(double value) { 523 return control.snapSpaceY(value); 524 } 525 526 /** 527 /** 528 * If this region's snapToPixel property is false, this method returns the 529 * same value, else it tries to return a value ceiled to the nearest 530 * pixel, but since there is no indication if the value is a vertical 531 * or horizontal measurement then it may be snapped to the wrong pixel 532 * size metric on screens with different horizontal and vertical scales. 533 * @param value the size value to be snapped 534 * @return value ceiled to nearest pixel 535 * @deprecated replaced by {@code snapSizeX()} and {@code snapSizeY()} 536 */ 537 protected double snapSize(double value) { 538 return control.snapSizeX(value); 539 } 540 541 /** 542 * If this region's snapToPixel property is true, returns a value ceiled 543 * to the nearest pixel in the horizontal direction, else returns the 544 * same value. 545 * @param value the size value to be snapped 546 * @return value ceiled to nearest pixel 547 * @since 9 548 */ 549 protected double snapSizeX(double value) { 550 return control.snapSizeX(value); 551 } 552 553 /** 554 * If this region's snapToPixel property is true, returns a value ceiled 555 * to the nearest pixel in the vertical direction, else returns the 556 * same value. 557 * @param value the size value to be snapped 558 * @return value ceiled to nearest pixel 559 * @since 9 560 */ 561 protected double snapSizeY(double value) { 562 return control.snapSizeY(value); 563 } 564 565 /** 566 * If this region's snapToPixel property is false, this method returns the 567 * same value, else it tries to return a value rounded to the nearest 568 * pixel, but since there is no indication if the value is a vertical 569 * or horizontal measurement then it may be snapped to the wrong pixel 570 * size metric on screens with different horizontal and vertical scales. 571 * @param value the position value to be snapped 572 * @return value rounded to nearest pixel 573 * @deprecated replaced by {@code snapPositionX()} and {@code snapPositionY()} 574 */ 575 protected double snapPosition(double value) { 576 return control.snapPositionX(value); 577 } 578 579 /** 580 * If this region's snapToPixel property is true, returns a value rounded 581 * to the nearest pixel in the horizontal direction, else returns the 582 * same value. 583 * @param value the position value to be snapped 584 * @return value rounded to nearest pixel 585 * @since 9 586 */ 587 protected double snapPositionX(double value) { 588 return control.snapPositionX(value); 589 } 590 591 /** 592 * If this region's snapToPixel property is true, returns a value rounded 593 * to the nearest pixel in the vertical direction, else returns the 594 * same value. 595 * @param value the position value to be snapped 596 * @return value rounded to nearest pixel 597 * @since 9 598 */ 599 protected double snapPositionY(double value) { 600 return control.snapPositionY(value); 601 } 602 603 /** 604 * Utility method which positions the child within an area of this 605 * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, 606 * with a baseline offset relative to that area. 607 * <p> 608 * This function does <i>not</i> resize the node and uses the node's layout bounds 609 * width and height to determine how it should be positioned within the area. 610 * <p> 611 * If the vertical alignment is {@code VPos.BASELINE} then it 612 * will position the node so that its own baseline aligns with the passed in 613 * {@code baselineOffset}, otherwise the baseline parameter is ignored. 614 * <p> 615 * If {@code snapToPixel} is {@code true} for this skin, then the x/y position 616 * values will be rounded to their nearest pixel boundaries. 617 * 618 * @param child the child being positioned within this skin 619 * @param areaX the horizontal offset of the layout area relative to this skin 620 * @param areaY the vertical offset of the layout area relative to this skin 621 * @param areaWidth the width of the layout area 622 * @param areaHeight the height of the layout area 623 * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE 624 * @param halignment the horizontal alignment for the child within the area 625 * @param valignment the vertical alignment for the child within the area 626 * 627 */ 628 protected void positionInArea(Node child, double areaX, double areaY, 629 double areaWidth, double areaHeight, double areaBaselineOffset, 630 HPos halignment, VPos valignment) { 631 positionInArea(child, areaX, areaY, areaWidth, areaHeight, 632 areaBaselineOffset, Insets.EMPTY, halignment, valignment); 633 } 634 635 /** 636 * Utility method which positions the child within an area of this 637 * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, 638 * with a baseline offset relative to that area. 639 * <p> 640 * This function does <i>not</i> resize the node and uses the node's layout bounds 641 * width and height to determine how it should be positioned within the area. 642 * <p> 643 * If the vertical alignment is {@code VPos.BASELINE} then it 644 * will position the node so that its own baseline aligns with the passed in 645 * {@code baselineOffset}, otherwise the baseline parameter is ignored. 646 * <p> 647 * If {@code snapToPixel} is {@code true} for this skin, then the x/y position 648 * values will be rounded to their nearest pixel boundaries. 649 * <p> 650 * If {@code margin} is non-null, then that space will be allocated around the 651 * child within the layout area. margin may be null. 652 * 653 * @param child the child being positioned within this skin 654 * @param areaX the horizontal offset of the layout area relative to this skin 655 * @param areaY the vertical offset of the layout area relative to this skin 656 * @param areaWidth the width of the layout area 657 * @param areaHeight the height of the layout area 658 * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE 659 * @param margin the margin of space to be allocated around the child 660 * @param halignment the horizontal alignment for the child within the area 661 * @param valignment the vertical alignment for the child within the area 662 * 663 * @since JavaFX 8.0 664 */ 665 protected void positionInArea(Node child, double areaX, double areaY, 666 double areaWidth, double areaHeight, double areaBaselineOffset, 667 Insets margin, HPos halignment, VPos valignment) { 668 Region.positionInArea(child, areaX, areaY, areaWidth, areaHeight, 669 areaBaselineOffset, margin, halignment, valignment, 670 control.isSnapToPixel()); 671 } 672 673 /** 674 * Utility method which lays out the child within an area of this 675 * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, 676 * with a baseline offset relative to that area. 677 * <p> 678 * If the child is resizable, this method will resize it to fill the specified 679 * area unless the node's maximum size prevents it. If the node's maximum 680 * size preference is less than the area size, the maximum size will be used. 681 * If node's maximum is greater than the area size, then the node will be 682 * resized to fit within the area, unless its minimum size prevents it. 683 * <p> 684 * If the child has a non-null contentBias, then this method will use it when 685 * resizing the child. If the contentBias is horizontal, it will set its width 686 * first to the area's width (up to the child's max width limit) and then pass 687 * that value to compute the child's height. If child's contentBias is vertical, 688 * then it will set its height to the area height (up to child's max height limit) 689 * and pass that height to compute the child's width. If the child's contentBias 690 * is null, then it's width and height have no dependencies on each other. 691 * <p> 692 * If the child is not resizable (Shape, Group, etc) then it will only be 693 * positioned and not resized. 694 * <p> 695 * If the child's resulting size differs from the area's size (either 696 * because it was not resizable or it's sizing preferences prevented it), then 697 * this function will align the node relative to the area using horizontal and 698 * vertical alignment values. 699 * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned 700 * with the area baseline offset parameter, otherwise the baseline parameter 701 * is ignored. 702 * <p> 703 * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y 704 * values will be rounded to their nearest pixel boundaries and the 705 * width/height values will be ceiled to the next pixel boundary. 706 * 707 * @param child the child being positioned within this skin 708 * @param areaX the horizontal offset of the layout area relative to this skin 709 * @param areaY the vertical offset of the layout area relative to this skin 710 * @param areaWidth the width of the layout area 711 * @param areaHeight the height of the layout area 712 * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE 713 * @param halignment the horizontal alignment for the child within the area 714 * @param valignment the vertical alignment for the child within the area 715 * 716 */ 717 protected void layoutInArea(Node child, double areaX, double areaY, 718 double areaWidth, double areaHeight, 719 double areaBaselineOffset, 720 HPos halignment, VPos valignment) { 721 layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, 722 Insets.EMPTY, true, true, halignment, valignment); 723 } 724 725 /** 726 * Utility method which lays out the child within an area of this 727 * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, 728 * with a baseline offset relative to that area. 729 * <p> 730 * If the child is resizable, this method will resize it to fill the specified 731 * area unless the node's maximum size prevents it. If the node's maximum 732 * size preference is less than the area size, the maximum size will be used. 733 * If node's maximum is greater than the area size, then the node will be 734 * resized to fit within the area, unless its minimum size prevents it. 735 * <p> 736 * If the child has a non-null contentBias, then this method will use it when 737 * resizing the child. If the contentBias is horizontal, it will set its width 738 * first to the area's width (up to the child's max width limit) and then pass 739 * that value to compute the child's height. If child's contentBias is vertical, 740 * then it will set its height to the area height (up to child's max height limit) 741 * and pass that height to compute the child's width. If the child's contentBias 742 * is null, then it's width and height have no dependencies on each other. 743 * <p> 744 * If the child is not resizable (Shape, Group, etc) then it will only be 745 * positioned and not resized. 746 * <p> 747 * If the child's resulting size differs from the area's size (either 748 * because it was not resizable or it's sizing preferences prevented it), then 749 * this function will align the node relative to the area using horizontal and 750 * vertical alignment values. 751 * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned 752 * with the area baseline offset parameter, otherwise the baseline parameter 753 * is ignored. 754 * <p> 755 * If {@code margin} is non-null, then that space will be allocated around the 756 * child within the layout area. margin may be null. 757 * <p> 758 * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y 759 * values will be rounded to their nearest pixel boundaries and the 760 * width/height values will be ceiled to the next pixel boundary. 761 * 762 * @param child the child being positioned within this skin 763 * @param areaX the horizontal offset of the layout area relative to this skin 764 * @param areaY the vertical offset of the layout area relative to this skin 765 * @param areaWidth the width of the layout area 766 * @param areaHeight the height of the layout area 767 * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE 768 * @param margin the margin of space to be allocated around the child 769 * @param halignment the horizontal alignment for the child within the area 770 * @param valignment the vertical alignment for the child within the area 771 */ 772 protected void layoutInArea(Node child, double areaX, double areaY, 773 double areaWidth, double areaHeight, 774 double areaBaselineOffset, 775 Insets margin, 776 HPos halignment, VPos valignment) { 777 layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, 778 margin, true, true, halignment, valignment); 779 } 780 781 /** 782 * Utility method which lays out the child within an area of this 783 * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, 784 * with a baseline offset relative to that area. 785 * <p> 786 * If the child is resizable, this method will use {@code fillWidth} and {@code fillHeight} 787 * to determine whether to resize it to fill the area or keep the child at its 788 * preferred dimension. If fillWidth/fillHeight are true, then this method 789 * will only resize the child up to its max size limits. If the node's maximum 790 * size preference is less than the area size, the maximum size will be used. 791 * If node's maximum is greater than the area size, then the node will be 792 * resized to fit within the area, unless its minimum size prevents it. 793 * <p> 794 * If the child has a non-null contentBias, then this method will use it when 795 * resizing the child. If the contentBias is horizontal, it will set its width 796 * first and then pass that value to compute the child's height. If child's 797 * contentBias is vertical, then it will set its height first 798 * and pass that value to compute the child's width. If the child's contentBias 799 * is null, then it's width and height have no dependencies on each other. 800 * <p> 801 * If the child is not resizable (Shape, Group, etc) then it will only be 802 * positioned and not resized. 803 * <p> 804 * If the child's resulting size differs from the area's size (either 805 * because it was not resizable or it's sizing preferences prevented it), then 806 * this function will align the node relative to the area using horizontal and 807 * vertical alignment values. 808 * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned 809 * with the area baseline offset parameter, otherwise the baseline parameter 810 * is ignored. 811 * <p> 812 * If {@code margin} is non-null, then that space will be allocated around the 813 * child within the layout area. margin may be null. 814 * <p> 815 * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y 816 * values will be rounded to their nearest pixel boundaries and the 817 * width/height values will be ceiled to the next pixel boundary. 818 * 819 * @param child the child being positioned within this skin 820 * @param areaX the horizontal offset of the layout area relative to this skin 821 * @param areaY the vertical offset of the layout area relative to this skin 822 * @param areaWidth the width of the layout area 823 * @param areaHeight the height of the layout area 824 * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE 825 * @param margin the margin of space to be allocated around the child 826 * @param fillWidth whether or not the child should be resized to fill the area width or kept to its preferred width 827 * @param fillHeight whether or not the child should e resized to fill the area height or kept to its preferred height 828 * @param halignment the horizontal alignment for the child within the area 829 * @param valignment the vertical alignment for the child within the area 830 */ 831 protected void layoutInArea(Node child, double areaX, double areaY, 832 double areaWidth, double areaHeight, 833 double areaBaselineOffset, 834 Insets margin, boolean fillWidth, boolean fillHeight, 835 HPos halignment, VPos valignment) { 836 Region.layoutInArea(child, areaX, areaY, areaWidth, areaHeight, 837 areaBaselineOffset, margin, fillWidth, fillHeight, halignment, 838 valignment, control.isSnapToPixel()); 839 } 840 841 842 843 /*************************************************************************** 844 * * 845 * Private Implementation * 846 * * 847 **************************************************************************/ 848 849 850 851 /************************************************************************** 852 * * 853 * Specialization of CSS handling code * 854 * * 855 **************************************************************************/ 856 857 private static class StyleableProperties { 858 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 859 860 static { 861 STYLEABLES = Collections.unmodifiableList(Control.getClassCssMetaData()); 862 } 863 } 864 865 /** 866 * Returns the CssMetaData associated with this class, which may include the 867 * CssMetaData of its super classes. 868 */ 869 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 870 return SkinBase.StyleableProperties.STYLEABLES; 871 } 872 873 /** 874 * This method should delegate to {@link Node#getClassCssMetaData()} so that 875 * a Node's CssMetaData can be accessed without the need for reflection. 876 * @return The CssMetaData associated with this node, which may include the 877 * CssMetaData of its super classes. 878 */ 879 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 880 return getClassCssMetaData(); 881 } 882 883 /** 884 * Used to specify that a pseudo-class of this Node has changed. If the 885 * pseudo-class is used in a CSS selector that matches this Node, CSS will 886 * be reapplied. Typically, this method is called from the {@code invalidated} 887 * method of a property that is used as a pseudo-class. For example: 888 * <code><pre> 889 * 890 * private static final PseudoClass MY_PSEUDO_CLASS_STATE = PseudoClass.getPseudoClass("my-state"); 891 * 892 * BooleanProperty myPseudoClassState = new BooleanPropertyBase(false) { 893 * 894 * {@literal @}Override public void invalidated() { 895 * pseudoClassStateChanged(MY_PSEUDO_CLASS_STATE, get()); 896 * } 897 * 898 * {@literal @}Override public Object getBean() { 899 * return MyControl.this; 900 * } 901 * 902 * {@literal @}Override public String getName() { 903 * return "myPseudoClassState"; 904 * } 905 * }; 906 * </pre><code> 907 * 908 * @see Node#pseudoClassStateChanged 909 * @param pseudoClass the pseudo-class that has changed state 910 * @param active whether or not the state is active 911 * @since JavaFX 8.0 912 */ 913 public final void pseudoClassStateChanged(PseudoClass pseudoClass, boolean active) { 914 Control ctl = getSkinnable(); 915 if (ctl != null) { 916 ctl.pseudoClassStateChanged(pseudoClass, active); 917 } 918 } 919 920 921 /*************************************************************************** 922 * * 923 * Accessibility handling * 924 * * 925 **************************************************************************/ 926 927 /** 928 * This method is called by the assistive technology to request 929 * the value for an attribute. 930 * <p> 931 * This method is commonly overridden by subclasses to implement 932 * attributes that are required for a specific role.<br> 933 * If a particular attribute is not handled, the super class implementation 934 * must be called. 935 * </p> 936 * 937 * @param attribute the requested attribute 938 * @param parameters optional list of parameters 939 * @return the value for the requested attribute 940 * 941 * @see AccessibleAttribute 942 * @see Node#queryAccessibleAttribute 943 * 944 * @since JavaFX 8u40 945 */ 946 protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { 947 return null; 948 } 949 950 /** 951 * This method is called by the assistive technology to request the action 952 * indicated by the argument should be executed. 953 * <p> 954 * This method is commonly overridden by subclasses to implement 955 * action that are required for a specific role.<br> 956 * If a particular action is not handled, the super class implementation 957 * must be called. 958 * </p> 959 * 960 * @param action the action to execute 961 * @param parameters optional list of parameters 962 * 963 * @see AccessibleAction 964 * @see Node#executeAccessibleAction 965 * 966 * @since JavaFX 8u40 967 */ 968 protected void executeAccessibleAction(AccessibleAction action, Object... parameters) { 969 } 970 971 /*************************************************************************** 972 * * 973 * Testing-only API * 974 * * 975 **************************************************************************/ 976 977 }