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