1 /* 2 * Copyright (c) 2012, 2014, 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 javafx.css.CssMetaData; 29 import javafx.css.PseudoClass; 30 import java.util.Collections; 31 import java.util.List; 32 import javafx.collections.ObservableList; 33 import javafx.css.Styleable; 34 import javafx.event.EventHandler; 35 import javafx.geometry.HPos; 36 import javafx.geometry.Insets; 37 import javafx.geometry.VPos; 38 import javafx.scene.AccessibleAction; 39 import javafx.scene.AccessibleAttribute; 40 import javafx.scene.Node; 41 import javafx.scene.input.MouseEvent; 42 import javafx.scene.layout.Region; 43 44 /** 45 * Base implementation class for defining the visual representation of user 46 * interface controls by defining a scene graph of nodes to represent the 47 * {@link Skin skin}. 48 * A user interface control is abstracted behind the {@link Skinnable} interface. 49 * 50 * @since JavaFX 8.0 51 */ 52 public abstract class SkinBase<C extends Control> implements Skin<C> { 53 54 /*************************************************************************** 55 * * 56 * Private fields * 57 * * 58 **************************************************************************/ 59 60 /** 61 * The {@code Control} that is referencing this Skin. There is a 62 * one-to-one relationship between a {@code Skin} and a {@code Control}. 63 * When a {@code Skin} is set on a {@code Control}, this variable is 64 * automatically updated. 65 */ 66 private C control; 67 68 /** 69 * A local field that directly refers to the children list inside the Control. 70 */ 71 private ObservableList<Node> children; 72 73 74 75 /*************************************************************************** 76 * * 77 * Event Handlers / Listeners * 78 * * 79 **************************************************************************/ 80 81 /** 82 * Mouse handler used for consuming all mouse events (preventing them 83 * from bubbling up to parent) 84 */ 85 private static final EventHandler<MouseEvent> mouseEventConsumer = event -> { 86 /* 87 ** we used to consume mouse wheel rotations here, 88 ** be we've switched to ScrollEvents, and only consume those which we use. 89 ** See RT-13995 & RT-14480 90 */ 91 event.consume(); 92 }; 93 94 95 96 /*************************************************************************** 97 * * 98 * Constructor * 99 * * 100 **************************************************************************/ 101 102 /** 103 * Constructor for all SkinBase instances. 104 * 105 * @param control The control for which this Skin should attach to. 106 */ 107 protected SkinBase(final C control) { 108 if (control == null) { 109 throw new IllegalArgumentException("Cannot pass null for control"); 110 } 111 112 // Update the control and behavior 113 this.control = control; 114 this.children = control.getControlChildren(); 115 116 // Default behavior for controls is to consume all mouse events 117 consumeMouseEvents(true); 118 } 119 120 121 122 /*************************************************************************** 123 * * 124 * Public API (from Skin) * 125 * * 126 **************************************************************************/ 127 128 /** {@inheritDoc} */ 129 @Override public final C getSkinnable() { 130 return control; 131 } 132 133 /** {@inheritDoc} */ 134 @Override public final Node getNode() { 135 return control; 136 } 137 138 /** {@inheritDoc} */ 139 @Override public void dispose() { 140 // control.removeEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler); 141 142 this.control = null; 143 } 144 145 146 147 /*************************************************************************** 148 * * 149 * Public API * 150 * * 151 **************************************************************************/ 152 153 /** 154 * Returns the children of the skin. 155 */ 156 public final ObservableList<Node> getChildren() { 157 return children; 158 } 159 160 /** 161 * Called during the layout pass of the scenegraph. 162 */ 163 protected void layoutChildren(final double contentX, final double contentY, 164 final double contentWidth, final double contentHeight) { 165 // By default simply sizes all managed children to fit within the space provided 166 for (int i=0, max=children.size(); i<max; i++) { 167 Node child = children.get(i); 168 if (child.isManaged()) { 169 layoutInArea(child, contentX, contentY, contentWidth, contentHeight, -1, HPos.CENTER, VPos.CENTER); 170 } 171 } 172 } 173 174 /** 175 * Determines whether all mouse events should be automatically consumed. 176 */ 177 protected final void consumeMouseEvents(boolean value) { 178 if (value) { 179 control.addEventHandler(MouseEvent.ANY, mouseEventConsumer); 180 } else { 181 control.removeEventHandler(MouseEvent.ANY, mouseEventConsumer); 182 } 183 } 184 185 186 187 /*************************************************************************** 188 * * 189 * Public Layout-related API * 190 * * 191 **************************************************************************/ 192 193 /** 194 * Computes the minimum allowable width of the Skin, based on the provided 195 * height. 196 * 197 * @param height The height of the Skin, in case this value might dictate 198 * the minimum width. 199 * @param topInset the pixel snapped top inset 200 * @param rightInset the pixel snapped right inset 201 * @param bottomInset the pixel snapped bottom inset 202 * @param leftInset the pixel snapped left inset 203 * @return A double representing the minimum width of this Skin. 204 */ 205 protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { 206 207 double minX = 0; 208 double maxX = 0; 209 boolean firstManagedChild = true; 210 for (int i = 0; i < children.size(); i++) { 211 Node node = children.get(i); 212 if (node.isManaged()) { 213 final double x = node.getLayoutBounds().getMinX() + node.getLayoutX(); 214 if (!firstManagedChild) { // branch prediction favors most often used condition 215 minX = Math.min(minX, x); 216 maxX = Math.max(maxX, x + node.minWidth(-1)); 217 } else { 218 minX = x; 219 maxX = x + node.minWidth(-1); 220 firstManagedChild = false; 221 } 222 } 223 } 224 double minWidth = maxX - minX; 225 return leftInset + minWidth + rightInset; 226 } 227 228 /** 229 * Computes the minimum allowable height of the Skin, based on the provided 230 * width. 231 * 232 * @param width The width of the Skin, in case this value might dictate 233 * the minimum height. 234 * @param topInset the pixel snapped top inset 235 * @param rightInset the pixel snapped right inset 236 * @param bottomInset the pixel snapped bottom inset 237 * @param leftInset the pixel snapped left inset 238 * @return A double representing the minimum height of this Skin. 239 */ 240 protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { 241 242 double minY = 0; 243 double maxY = 0; 244 boolean firstManagedChild = true; 245 for (int i = 0; i < children.size(); i++) { 246 Node node = children.get(i); 247 if (node.isManaged()) { 248 final double y = node.getLayoutBounds().getMinY() + node.getLayoutY(); 249 if (!firstManagedChild) { // branch prediction favors most often used condition 250 minY = Math.min(minY, y); 251 maxY = Math.max(maxY, y + node.minHeight(-1)); 252 } else { 253 minY = y; 254 maxY = y + node.minHeight(-1); 255 firstManagedChild = false; 256 } 257 } 258 } 259 double minHeight = maxY - minY; 260 return topInset + minHeight + bottomInset; 261 } 262 263 /** 264 * Computes the maximum allowable width of the Skin, based on the provided 265 * height. 266 * 267 * @param height The height of the Skin, in case this value might dictate 268 * the maximum width. 269 * @param topInset the pixel snapped top inset 270 * @param rightInset the pixel snapped right inset 271 * @param bottomInset the pixel snapped bottom inset 272 * @param leftInset the pixel snapped left inset 273 * @return A double representing the maximum width of this Skin. 274 */ 275 protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { 276 return Double.MAX_VALUE; 277 } 278 279 /** 280 * Computes the maximum allowable height of the Skin, based on the provided 281 * width. 282 * 283 * @param width The width of the Skin, in case this value might dictate 284 * the maximum height. 285 * @param topInset the pixel snapped top inset 286 * @param rightInset the pixel snapped right inset 287 * @param bottomInset the pixel snapped bottom inset 288 * @param leftInset the pixel snapped left inset 289 * @return A double representing the maximum height of this Skin. 290 */ 291 protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { 292 return Double.MAX_VALUE; 293 } 294 295 // PENDING_DOC_REVIEW 296 /** 297 * Calculates the preferred width of this {@code SkinBase}. The default 298 * implementation calculates this width as the width of the area occupied 299 * by its managed children when they are positioned at their 300 * current positions at their preferred widths. 301 * 302 * @param height the height that should be used if preferred width depends on it 303 * @param topInset the pixel snapped top inset 304 * @param rightInset the pixel snapped right inset 305 * @param bottomInset the pixel snapped bottom inset 306 * @param leftInset the pixel snapped left inset 307 * @return the calculated preferred width 308 */ 309 protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { 310 311 double minX = 0; 312 double maxX = 0; 313 boolean firstManagedChild = true; 314 for (int i = 0; i < children.size(); i++) { 315 Node node = children.get(i); 316 if (node.isManaged()) { 317 final double x = node.getLayoutBounds().getMinX() + node.getLayoutX(); 318 if (!firstManagedChild) { // branch prediction favors most often used condition 319 minX = Math.min(minX, x); 320 maxX = Math.max(maxX, x + node.prefWidth(-1)); 321 } else { 322 minX = x; 323 maxX = x + node.prefWidth(-1); 324 firstManagedChild = false; 325 } 326 } 327 } 328 return maxX - minX; 329 } 330 331 // PENDING_DOC_REVIEW 332 /** 333 * Calculates the preferred height of this {@code SkinBase}. The default 334 * implementation calculates this height as the height of the area occupied 335 * by its managed children when they are positioned at their current 336 * positions at their preferred heights. 337 * 338 * @param width the width that should be used if preferred height depends on it 339 * @param topInset the pixel snapped top inset 340 * @param rightInset the pixel snapped right inset 341 * @param bottomInset the pixel snapped bottom inset 342 * @param leftInset the pixel snapped left inset 343 * @return the calculated preferred height 344 */ 345 protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { 346 347 double minY = 0; 348 double maxY = 0; 349 boolean firstManagedChild = true; 350 for (int i = 0; i < children.size(); i++) { 351 Node node = children.get(i); 352 if (node.isManaged()) { 353 final double y = node.getLayoutBounds().getMinY() + node.getLayoutY(); 354 if (!firstManagedChild) { // branch prediction favors most often used condition 355 minY = Math.min(minY, y); 356 maxY = Math.max(maxY, y + node.prefHeight(-1)); 357 } else { 358 minY = y; 359 maxY = y + node.prefHeight(-1); 360 firstManagedChild = false; 361 } 362 } 363 } 364 return maxY - minY; 365 } 366 367 /** 368 * Calculates the baseline offset based on the first managed child. If there 369 * is no such child, returns {@link Node#getBaselineOffset()}. 370 * 371 * @param topInset the pixel snapped top inset 372 * @param rightInset the pixel snapped right inset 373 * @param bottomInset the pixel snapped bottom inset 374 * @param leftInset the pixel snapped left inset 375 * @return baseline offset 376 */ 377 protected double computeBaselineOffset(double topInset, double rightInset, double bottomInset, double leftInset) { 378 int size = children.size(); 379 for (int i = 0; i < size; ++i) { 380 Node child = children.get(i); 381 if (child.isManaged()) { 382 double offset = child.getBaselineOffset(); 383 if (offset == Node.BASELINE_OFFSET_SAME_AS_HEIGHT) { 384 continue; 385 } 386 return child.getLayoutBounds().getMinY() + child.getLayoutY() + offset; 387 } 388 } 389 return Node.BASELINE_OFFSET_SAME_AS_HEIGHT; 390 } 391 392 393 /*************************************************************************** 394 * * 395 * (Mostly ugly) Skin -> Control forwarding API * 396 * * 397 **************************************************************************/ 398 399 /** 400 * Utility method to get the top inset which includes padding and border 401 * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true. 402 * 403 * @return Rounded up insets top 404 */ 405 protected double snappedTopInset() { 406 return control.snappedTopInset(); 407 } 408 409 /** 410 * Utility method to get the bottom inset which includes padding and border 411 * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true. 412 * 413 * @return Rounded up insets bottom 414 */ 415 protected double snappedBottomInset() { 416 return control.snappedBottomInset(); 417 } 418 419 /** 420 * Utility method to get the left inset which includes padding and border 421 * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true. 422 * 423 * @return Rounded up insets left 424 */ 425 protected double snappedLeftInset() { 426 return control.snappedLeftInset(); 427 } 428 429 /** 430 * Utility method to get the right inset which includes padding and border 431 * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true. 432 * 433 * @return Rounded up insets right 434 */ 435 protected double snappedRightInset() { 436 return control.snappedRightInset(); 437 } 438 439 /** 440 * If this region's snapToPixel property is true, returns a value rounded 441 * to the nearest pixel, else returns the same value. 442 * @param value the space value to be snapped 443 * @return value rounded to nearest pixel 444 */ 445 protected double snapSpace(double value) { 446 return control.isSnapToPixel() ? Math.round(value) : value; 447 } 448 449 /** 450 * If this region's snapToPixel property is true, returns a value ceiled 451 * to the nearest pixel, else returns the same value. 452 * @param value the size value to be snapped 453 * @return value ceiled to nearest pixel 454 */ 455 protected double snapSize(double value) { 456 return control.isSnapToPixel() ? Math.ceil(value) : value; 457 } 458 459 /** 460 * If this region's snapToPixel property is true, returns a value rounded 461 * to the nearest pixel, else returns the same value. 462 * @param value the position value to be snapped 463 * @return value rounded to nearest pixel 464 */ 465 protected double snapPosition(double value) { 466 return control.isSnapToPixel() ? Math.round(value) : value; 467 } 468 469 /** 470 * Utility method which positions the child within an area of this 471 * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, 472 * with a baseline offset relative to that area. 473 * <p> 474 * This function does <i>not</i> resize the node and uses the node's layout bounds 475 * width and height to determine how it should be positioned within the area. 476 * <p> 477 * If the vertical alignment is {@code VPos.BASELINE} then it 478 * will position the node so that its own baseline aligns with the passed in 479 * {@code baselineOffset}, otherwise the baseline parameter is ignored. 480 * <p> 481 * If {@code snapToPixel} is {@code true} for this skin, then the x/y position 482 * values will be rounded to their nearest pixel boundaries. 483 * 484 * @param child the child being positioned within this skin 485 * @param areaX the horizontal offset of the layout area relative to this skin 486 * @param areaY the vertical offset of the layout area relative to this skin 487 * @param areaWidth the width of the layout area 488 * @param areaHeight the height of the layout area 489 * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE 490 * @param halignment the horizontal alignment for the child within the area 491 * @param valignment the vertical alignment for the child within the area 492 * 493 */ 494 protected void positionInArea(Node child, double areaX, double areaY, 495 double areaWidth, double areaHeight, double areaBaselineOffset, 496 HPos halignment, VPos valignment) { 497 positionInArea(child, areaX, areaY, areaWidth, areaHeight, 498 areaBaselineOffset, Insets.EMPTY, halignment, valignment); 499 } 500 501 /** 502 * Utility method which positions the child within an area of this 503 * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, 504 * with a baseline offset relative to that area. 505 * <p> 506 * This function does <i>not</i> resize the node and uses the node's layout bounds 507 * width and height to determine how it should be positioned within the area. 508 * <p> 509 * If the vertical alignment is {@code VPos.BASELINE} then it 510 * will position the node so that its own baseline aligns with the passed in 511 * {@code baselineOffset}, otherwise the baseline parameter is ignored. 512 * <p> 513 * If {@code snapToPixel} is {@code true} for this skin, then the x/y position 514 * values will be rounded to their nearest pixel boundaries. 515 * <p> 516 * If {@code margin} is non-null, then that space will be allocated around the 517 * child within the layout area. margin may be null. 518 * 519 * @param child the child being positioned within this skin 520 * @param areaX the horizontal offset of the layout area relative to this skin 521 * @param areaY the vertical offset of the layout area relative to this skin 522 * @param areaWidth the width of the layout area 523 * @param areaHeight the height of the layout area 524 * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE 525 * @param margin the margin of space to be allocated around the child 526 * @param halignment the horizontal alignment for the child within the area 527 * @param valignment the vertical alignment for the child within the area 528 * 529 * @since JavaFX 8.0 530 */ 531 protected void positionInArea(Node child, double areaX, double areaY, 532 double areaWidth, double areaHeight, double areaBaselineOffset, 533 Insets margin, HPos halignment, VPos valignment) { 534 Region.positionInArea(child, areaX, areaY, areaWidth, areaHeight, 535 areaBaselineOffset, margin, halignment, valignment, 536 control.isSnapToPixel()); 537 } 538 539 /** 540 * Utility method which lays out the child within an area of this 541 * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, 542 * with a baseline offset relative to that area. 543 * <p> 544 * If the child is resizable, this method will resize it to fill the specified 545 * area unless the node's maximum size prevents it. If the node's maximum 546 * size preference is less than the area size, the maximum size will be used. 547 * If node's maximum is greater than the area size, then the node will be 548 * resized to fit within the area, unless its minimum size prevents it. 549 * <p> 550 * If the child has a non-null contentBias, then this method will use it when 551 * resizing the child. If the contentBias is horizontal, it will set its width 552 * first to the area's width (up to the child's max width limit) and then pass 553 * that value to compute the child's height. If child's contentBias is vertical, 554 * then it will set its height to the area height (up to child's max height limit) 555 * and pass that height to compute the child's width. If the child's contentBias 556 * is null, then it's width and height have no dependencies on each other. 557 * <p> 558 * If the child is not resizable (Shape, Group, etc) then it will only be 559 * positioned and not resized. 560 * <p> 561 * If the child's resulting size differs from the area's size (either 562 * because it was not resizable or it's sizing preferences prevented it), then 563 * this function will align the node relative to the area using horizontal and 564 * vertical alignment values. 565 * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned 566 * with the area baseline offset parameter, otherwise the baseline parameter 567 * is ignored. 568 * <p> 569 * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y 570 * values will be rounded to their nearest pixel boundaries and the 571 * width/height values will be ceiled to the next pixel boundary. 572 * 573 * @param child the child being positioned within this skin 574 * @param areaX the horizontal offset of the layout area relative to this skin 575 * @param areaY the vertical offset of the layout area relative to this skin 576 * @param areaWidth the width of the layout area 577 * @param areaHeight the height of the layout area 578 * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE 579 * @param halignment the horizontal alignment for the child within the area 580 * @param valignment the vertical alignment for the child within the area 581 * 582 */ 583 protected void layoutInArea(Node child, double areaX, double areaY, 584 double areaWidth, double areaHeight, 585 double areaBaselineOffset, 586 HPos halignment, VPos valignment) { 587 layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, 588 Insets.EMPTY, true, true, halignment, valignment); 589 } 590 591 /** 592 * Utility method which lays out the child within an area of this 593 * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, 594 * with a baseline offset relative to that area. 595 * <p> 596 * If the child is resizable, this method will resize it to fill the specified 597 * area unless the node's maximum size prevents it. If the node's maximum 598 * size preference is less than the area size, the maximum size will be used. 599 * If node's maximum is greater than the area size, then the node will be 600 * resized to fit within the area, unless its minimum size prevents it. 601 * <p> 602 * If the child has a non-null contentBias, then this method will use it when 603 * resizing the child. If the contentBias is horizontal, it will set its width 604 * first to the area's width (up to the child's max width limit) and then pass 605 * that value to compute the child's height. If child's contentBias is vertical, 606 * then it will set its height to the area height (up to child's max height limit) 607 * and pass that height to compute the child's width. If the child's contentBias 608 * is null, then it's width and height have no dependencies on each other. 609 * <p> 610 * If the child is not resizable (Shape, Group, etc) then it will only be 611 * positioned and not resized. 612 * <p> 613 * If the child's resulting size differs from the area's size (either 614 * because it was not resizable or it's sizing preferences prevented it), then 615 * this function will align the node relative to the area using horizontal and 616 * vertical alignment values. 617 * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned 618 * with the area baseline offset parameter, otherwise the baseline parameter 619 * is ignored. 620 * <p> 621 * If {@code margin} is non-null, then that space will be allocated around the 622 * child within the layout area. margin may be null. 623 * <p> 624 * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y 625 * values will be rounded to their nearest pixel boundaries and the 626 * width/height values will be ceiled to the next pixel boundary. 627 * 628 * @param child the child being positioned within this skin 629 * @param areaX the horizontal offset of the layout area relative to this skin 630 * @param areaY the vertical offset of the layout area relative to this skin 631 * @param areaWidth the width of the layout area 632 * @param areaHeight the height of the layout area 633 * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE 634 * @param margin the margin of space to be allocated around the child 635 * @param halignment the horizontal alignment for the child within the area 636 * @param valignment the vertical alignment for the child within the area 637 */ 638 protected void layoutInArea(Node child, double areaX, double areaY, 639 double areaWidth, double areaHeight, 640 double areaBaselineOffset, 641 Insets margin, 642 HPos halignment, VPos valignment) { 643 layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, 644 margin, true, true, halignment, valignment); 645 } 646 647 /** 648 * Utility method which lays out the child within an area of this 649 * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, 650 * with a baseline offset relative to that area. 651 * <p> 652 * If the child is resizable, this method will use {@code fillWidth} and {@code fillHeight} 653 * to determine whether to resize it to fill the area or keep the child at its 654 * preferred dimension. If fillWidth/fillHeight are true, then this method 655 * will only resize the child up to its max size limits. If the node's maximum 656 * size preference is less than the area size, the maximum size will be used. 657 * If node's maximum is greater than the area size, then the node will be 658 * resized to fit within the area, unless its minimum size prevents it. 659 * <p> 660 * If the child has a non-null contentBias, then this method will use it when 661 * resizing the child. If the contentBias is horizontal, it will set its width 662 * first and then pass that value to compute the child's height. If child's 663 * contentBias is vertical, then it will set its height first 664 * and pass that value to compute the child's width. If the child's contentBias 665 * is null, then it's width and height have no dependencies on each other. 666 * <p> 667 * If the child is not resizable (Shape, Group, etc) then it will only be 668 * positioned and not resized. 669 * <p> 670 * If the child's resulting size differs from the area's size (either 671 * because it was not resizable or it's sizing preferences prevented it), then 672 * this function will align the node relative to the area using horizontal and 673 * vertical alignment values. 674 * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned 675 * with the area baseline offset parameter, otherwise the baseline parameter 676 * is ignored. 677 * <p> 678 * If {@code margin} is non-null, then that space will be allocated around the 679 * child within the layout area. margin may be null. 680 * <p> 681 * If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y 682 * values will be rounded to their nearest pixel boundaries and the 683 * width/height values will be ceiled to the next pixel boundary. 684 * 685 * @param child the child being positioned within this skin 686 * @param areaX the horizontal offset of the layout area relative to this skin 687 * @param areaY the vertical offset of the layout area relative to this skin 688 * @param areaWidth the width of the layout area 689 * @param areaHeight the height of the layout area 690 * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE 691 * @param margin the margin of space to be allocated around the child 692 * @param fillWidth whether or not the child should be resized to fill the area width or kept to its preferred width 693 * @param fillHeight whether or not the child should e resized to fill the area height or kept to its preferred height 694 * @param halignment the horizontal alignment for the child within the area 695 * @param valignment the vertical alignment for the child within the area 696 */ 697 protected void layoutInArea(Node child, double areaX, double areaY, 698 double areaWidth, double areaHeight, 699 double areaBaselineOffset, 700 Insets margin, boolean fillWidth, boolean fillHeight, 701 HPos halignment, VPos valignment) { 702 Region.layoutInArea(child, areaX, areaY, areaWidth, areaHeight, 703 areaBaselineOffset, margin, fillWidth, fillHeight, halignment, 704 valignment, control.isSnapToPixel()); 705 } 706 707 708 709 /*************************************************************************** 710 * * 711 * Private Implementation * 712 * * 713 **************************************************************************/ 714 715 716 717 /************************************************************************** 718 * * 719 * Specialization of CSS handling code * 720 * * 721 **************************************************************************/ 722 723 private static class StyleableProperties { 724 725 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 726 727 static { 728 STYLEABLES = Collections.unmodifiableList(Control.getClassCssMetaData()); 729 } 730 } 731 732 /** 733 * @return The CssMetaData associated with this class, which may include the 734 * CssMetaData of its super classes. 735 */ public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 736 return SkinBase.StyleableProperties.STYLEABLES; 737 } 738 739 /** 740 * This method should delegate to {@link Node#getClassCssMetaData()} so that 741 * a Node's CssMetaData can be accessed without the need for reflection. 742 * @return The CssMetaData associated with this node, which may include the 743 * CssMetaData of its super classes. 744 */ 745 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 746 return getClassCssMetaData(); 747 } 748 749 /** @see Node#pseudoClassStateChanged */ 750 public final void pseudoClassStateChanged(PseudoClass pseudoClass, boolean active) { 751 Control ctl = getSkinnable(); 752 if (ctl != null) { 753 ctl.pseudoClassStateChanged(pseudoClass, active); 754 } 755 } 756 757 758 /*************************************************************************** 759 * * 760 * Accessibility handling * 761 * * 762 **************************************************************************/ 763 764 /** @see Node#queryAccessibleAttribute */ 765 protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { 766 return null; 767 } 768 769 /** @see Node#executeAccessibleAction */ 770 protected void executeAccessibleAction(AccessibleAction action, Object... parameters) { 771 } 772 773 /*************************************************************************** 774 * * 775 * Testing-only API * 776 * * 777 **************************************************************************/ 778 779 }