All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javafx.scene.control.SkinBase Maven / Gradle / Ivy

There is a newer version: 24-ea+15
Show newest version
/*
 * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javafx.scene.control;

import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;

import javafx.beans.Observable;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.css.CssMetaData;
import javafx.css.PseudoClass;
import javafx.css.Styleable;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.AccessibleAction;
import javafx.scene.AccessibleAttribute;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;

import com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler;
import com.sun.javafx.scene.control.ListenerHelper;

/**
 * Base implementation class for defining the visual representation of user
 * interface controls by defining a scene graph of nodes to represent the
 * {@link Skin skin}.
 * A user interface control is abstracted behind the {@link Skinnable} interface.
 *
 * @param  the type of the control
 * @since JavaFX 8.0
 */
public abstract class SkinBase implements Skin {

    static {
        // must be the first code to execute
        ListenerHelper.setAccessor((skin) -> skin.listenerHelper());
    }

    /* *************************************************************************
     *                                                                         *
     * Private fields                                                          *
     *                                                                         *
     **************************************************************************/

    /**
     * The {@code Control} that is referencing this Skin. There is a
     * one-to-one relationship between a {@code Skin} and a {@code Control}.
     * When a {@code Skin} is set on a {@code Control}, this variable is
     * automatically updated.
     */
    private C control;

    /**
     * A local field that directly refers to the children list inside the Control.
     */
    private ObservableList children;

    /**
     * This is part of the workaround introduced during delomboking. We probably will
     * want to adjust the way listeners are added rather than continuing to use this
     * map (although it doesn't really do much harm).
     *
     * TODO remove after migration to ListenerHelper
     */
    private LambdaMultiplePropertyChangeListenerHandler lambdaChangeListenerHandler;

    private ListenerHelper listenerHelper;

    /* *************************************************************************
     *                                                                         *
     * Event Handlers / Listeners                                              *
     *                                                                         *
     **************************************************************************/

    /**
     * Mouse handler used for consuming all mouse events (preventing them
     * from bubbling up to parent)
     */
    private static final EventHandler mouseEventConsumer = event -> {
        /*
        ** we used to consume mouse wheel rotations here,
        ** be we've switched to ScrollEvents, and only consume those which we use.
        ** See RT-13995 & RT-14480
        */
        event.consume();
    };



    /* *************************************************************************
     *                                                                         *
     * Constructor                                                             *
     *                                                                         *
     **************************************************************************/

    /**
     * Constructor for all SkinBase instances.
     *
     * @param control The control for which this Skin should attach to.
     */
    protected SkinBase(final C control) {
        if (control == null) {
            throw new IllegalArgumentException("Cannot pass null for control");
        }

        // Update the control and behavior
        this.control = control;
        this.children = control.getControlChildren();

        // Default behavior for controls is to consume all mouse events
        consumeMouseEvents(true);
    }



    /* *************************************************************************
     *                                                                         *
     * Public API (from Skin)                                                  *
     *                                                                         *
     **************************************************************************/

    /** {@inheritDoc} */
    @Override public final C getSkinnable() {
        return control;
    }

    /** {@inheritDoc} */
    @Override public final Node getNode() {
        return control;
    }

    /** {@inheritDoc} */
    @Override public void dispose() {
//        control.removeEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler);

        // unhook listeners
        if (lambdaChangeListenerHandler != null) {
            lambdaChangeListenerHandler.dispose();
        }

        if (listenerHelper != null) {
            listenerHelper.disconnect();
        }

        this.control = null;
    }



    /* *************************************************************************
     *                                                                         *
     * Public API                                                              *
     *                                                                         *
     **************************************************************************/

    /**
     * Returns the children of the skin.
     * @return the children of the skin
     */
    public final ObservableList getChildren() {
        return children;
    }

    /**
     * Called during the layout pass of the scenegraph.
     * @param contentX the x position
     * @param contentY the y position
     * @param contentWidth the width
     * @param contentHeight the height
     */
    protected void layoutChildren(final double contentX, final double contentY,
            final double contentWidth, final double contentHeight) {
        // By default simply sizes all managed children to fit within the space provided
        for (int i=0, max=children.size(); i observable, Consumer> operation) {
        if (lambdaChangeListenerHandler == null) {
            lambdaChangeListenerHandler = new LambdaMultiplePropertyChangeListenerHandler();
        }
        lambdaChangeListenerHandler.registerChangeListener(observable, operation);
    }

    /**
     * Unregisters all operations that have been registered using
     * {@link #registerChangeListener(ObservableValue, Consumer)}
     * for the given {@code observable}. Does nothing if {@code observable} is {@code null}.
     *
     * @param observable the observable for which the registered operations should be removed,
     *  may be {@code null}
     * @return a composed consumer representing all previously registered operations, or
     *  {@code null} if none have been registered or the observable is {@code null}
     * @since 9
     */
    protected final Consumer> unregisterChangeListeners(ObservableValue observable) {
        if (lambdaChangeListenerHandler == null) {
            return null;
        }
        return lambdaChangeListenerHandler.unregisterChangeListeners(observable);
    }

    /**
     * Registers an operation to perform when the given {@code observable} sends an invalidation event.
     * Does nothing if either {@code observable} or {@code operation} are {@code null}.
     * If multiple operations are registered on the same observable, they will be performed in the
     * order in which they were registered.
     *
     * @param observable the observable to observe for invalidation events, may be {@code null}
     * @param operation the operation to perform when the observable sends an invalidation event,
     *  may be {@code null}
     * @since 17
     */
    protected final void registerInvalidationListener(Observable observable, Consumer operation) {
        if (lambdaChangeListenerHandler == null) {
            lambdaChangeListenerHandler = new LambdaMultiplePropertyChangeListenerHandler();
        }
        lambdaChangeListenerHandler.registerInvalidationListener(observable, operation);
    }

    /**
     * Unregisters all operations that have been registered using
     * {@link #registerInvalidationListener(Observable, Consumer)}
     * for the given {@code observable}. Does nothing if {@code observable} is {@code null}.
     *
     * @param observable the observable for which the registered operations should be removed,
     *  may be {@code null}
     * @return a composed consumer representing all previously registered operations, or
     *  {@code null} if none have been registered or the observable is {@code null}
     * @since 17
     */
    protected final Consumer unregisterInvalidationListeners(Observable observable) {
        if (lambdaChangeListenerHandler == null) {
            return null;
        }
        return lambdaChangeListenerHandler.unregisterInvalidationListeners(observable);
    }


    /**
     * Registers an operation to perform when the given {@code observableList} sends a list change event.
     * Does nothing if either {@code observableList} or {@code operation} are {@code null}.
     * If multiple operations are registered on the same observable list, they will be performed in the
     * order in which they were registered.
     *
     * @param observableList the observableList to observe for list change events, may be {@code null}
     * @param operation the operation to perform when the observableList sends a list change event,
     *  may be {@code null}
     * @since 17
     */
    protected final void registerListChangeListener(ObservableList observableList, Consumer> operation) {
        if (lambdaChangeListenerHandler == null) {
            lambdaChangeListenerHandler = new LambdaMultiplePropertyChangeListenerHandler();
        }
        lambdaChangeListenerHandler.registerListChangeListener(observableList, operation);
    }

    /**
     * Unregisters all operations that have been registered using
     * {@link #registerListChangeListener(ObservableList, Consumer)}
     * for the given {@code observableList}. Does nothing if {@code observableList} is {@code null}.
     *
     * @param observableList the observableList for which the registered operations should be removed,
     *  may be {@code null}
     * @return a composed consumer representing all previously registered operations, or
     *  {@code null} if none have been registered or the observableList is {@code null}
     * @since 17
     */
    protected final Consumer> unregisterListChangeListeners(ObservableList observableList) {
        if (lambdaChangeListenerHandler == null) {
            return null;
        }
        return lambdaChangeListenerHandler.unregisterListChangeListeners(observableList);
    }


    /* *************************************************************************
     *                                                                         *
     * Public Layout-related API                                               *
     *                                                                         *
     **************************************************************************/

    /**
     * Computes the minimum allowable width of the Skin, based on the provided
     * height.
     *
     * @param height The height of the Skin, in case this value might dictate
     *      the minimum width.
     * @param topInset the pixel snapped top inset
     * @param rightInset the pixel snapped right inset
     * @param bottomInset the pixel snapped bottom inset
     * @param leftInset  the pixel snapped left inset
     * @return A double representing the minimum width of this Skin.
     */
    protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {

        double minX = 0;
        double maxX = 0;
        boolean firstManagedChild = true;
        for (int i = 0; i < children.size(); i++) {
            Node node = children.get(i);
            if (node.isManaged()) {
                final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
                if (!firstManagedChild) {  // branch prediction favors most often used condition
                    minX = Math.min(minX, x);
                    maxX = Math.max(maxX, x + node.minWidth(-1));
                } else {
                    minX = x;
                    maxX = x + node.minWidth(-1);
                    firstManagedChild = false;
                }
            }
        }
        double minWidth = maxX - minX;
        return leftInset + minWidth + rightInset;
    }

    /**
     * Computes the minimum allowable height of the Skin, based on the provided
     * width.
     *
     * @param width The width of the Skin, in case this value might dictate
     *      the minimum height.
     * @param topInset the pixel snapped top inset
     * @param rightInset the pixel snapped right inset
     * @param bottomInset the pixel snapped bottom inset
     * @param leftInset  the pixel snapped left inset
     * @return A double representing the minimum height of this Skin.
     */
    protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {

        double minY = 0;
        double maxY = 0;
        boolean firstManagedChild = true;
        for (int i = 0; i < children.size(); i++) {
            Node node = children.get(i);
            if (node.isManaged()) {
                final double y = node.getLayoutBounds().getMinY() + node.getLayoutY();
                if (!firstManagedChild) {  // branch prediction favors most often used condition
                    minY = Math.min(minY, y);
                    maxY = Math.max(maxY, y + node.minHeight(-1));
                } else {
                    minY = y;
                    maxY = y + node.minHeight(-1);
                    firstManagedChild = false;
                }
            }
        }
        double minHeight = maxY - minY;
        return topInset + minHeight + bottomInset;
    }

    /**
     * Computes the maximum allowable width of the Skin, based on the provided
     * height.
     *
     * @param height The height of the Skin, in case this value might dictate
     *      the maximum width.
     * @param topInset the pixel snapped top inset
     * @param rightInset the pixel snapped right inset
     * @param bottomInset the pixel snapped bottom inset
     * @param leftInset  the pixel snapped left inset
     * @return A double representing the maximum width of this Skin.
     */
    protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
        return Double.MAX_VALUE;
    }

    /**
     * Computes the maximum allowable height of the Skin, based on the provided
     * width.
     *
     * @param width The width of the Skin, in case this value might dictate
     *      the maximum height.
     * @param topInset the pixel snapped top inset
     * @param rightInset the pixel snapped right inset
     * @param bottomInset the pixel snapped bottom inset
     * @param leftInset  the pixel snapped left inset
     * @return A double representing the maximum height of this Skin.
     */
    protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
        return Double.MAX_VALUE;
    }

    // PENDING_DOC_REVIEW
    /**
     * Calculates the preferred width of this {@code SkinBase}. The default
     * implementation calculates this width as the width of the area occupied
     * by its managed children when they are positioned at their
     * current positions at their preferred widths.
     *
     * @param height the height that should be used if preferred width depends on it
     * @param topInset the pixel snapped top inset
     * @param rightInset the pixel snapped right inset
     * @param bottomInset the pixel snapped bottom inset
     * @param leftInset  the pixel snapped left inset
     * @return the calculated preferred width
     */
    protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {

        double minX = 0;
        double maxX = 0;
        boolean firstManagedChild = true;
        for (int i = 0; i < children.size(); i++) {
            Node node = children.get(i);
            if (node.isManaged()) {
                final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
                if (!firstManagedChild) {  // branch prediction favors most often used condition
                    minX = Math.min(minX, x);
                    maxX = Math.max(maxX, x + node.prefWidth(-1));
                } else {
                    minX = x;
                    maxX = x + node.prefWidth(-1);
                    firstManagedChild = false;
                }
            }
        }
        return maxX - minX;
    }

    // PENDING_DOC_REVIEW
    /**
     * Calculates the preferred height of this {@code SkinBase}. The default
     * implementation calculates this height as the height of the area occupied
     * by its managed children when they are positioned at their current
     * positions at their preferred heights.
     *
     * @param width the width that should be used if preferred height depends on it
     * @param topInset the pixel snapped top inset
     * @param rightInset the pixel snapped right inset
     * @param bottomInset the pixel snapped bottom inset
     * @param leftInset  the pixel snapped left inset
     * @return the calculated preferred height
     */
    protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {

        double minY = 0;
        double maxY = 0;
        boolean firstManagedChild = true;
        for (int i = 0; i < children.size(); i++) {
            Node node = children.get(i);
            if (node.isManaged()) {
                final double y = node.getLayoutBounds().getMinY() + node.getLayoutY();
                if (!firstManagedChild) {  // branch prediction favors most often used condition
                    minY = Math.min(minY, y);
                    maxY = Math.max(maxY, y + node.prefHeight(-1));
                } else {
                    minY = y;
                    maxY = y + node.prefHeight(-1);
                    firstManagedChild = false;
                }
            }
        }
        return maxY - minY;
    }

    /**
     * Calculates the baseline offset based on the first managed child. If there
     * is no such child, returns {@link Node#getBaselineOffset()}.
     *
     * @param topInset the pixel snapped top inset
     * @param rightInset the pixel snapped right inset
     * @param bottomInset the pixel snapped bottom inset
     * @param leftInset  the pixel snapped left inset
     * @return baseline offset
     */
    protected double computeBaselineOffset(double topInset, double rightInset, double bottomInset, double leftInset) {
        int size = children.size();
        for (int i = 0; i < size; ++i) {
            Node child = children.get(i);
            if (child.isManaged()) {
                double offset = child.getBaselineOffset();
                if (offset == Node.BASELINE_OFFSET_SAME_AS_HEIGHT) {
                    continue;
                }
                return child.getLayoutBounds().getMinY() + child.getLayoutY() + offset;
            }
        }
        return Node.BASELINE_OFFSET_SAME_AS_HEIGHT;
    }


    /* *************************************************************************
     *                                                                         *
     * (Mostly ugly) Skin -> Control forwarding API                            *
     *                                                                         *
     **************************************************************************/

    /**
     * Utility method to get the top inset which includes padding and border
     * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
     *
     * @return Rounded up insets top
     */
    protected double snappedTopInset() {
        return control.snappedTopInset();
    }

    /**
     * Utility method to get the bottom inset which includes padding and border
     * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
     *
     * @return Rounded up insets bottom
     */
    protected double snappedBottomInset() {
        return control.snappedBottomInset();
    }

    /**
     * Utility method to get the left inset which includes padding and border
     * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
     *
     * @return Rounded up insets left
     */
    protected double snappedLeftInset() {
        return control.snappedLeftInset();
    }

    /**
     * Utility method to get the right inset which includes padding and border
     * inset. Then snapped to whole pixels if getSkinnable().isSnapToPixel() is true.
     *
     * @return Rounded up insets right
     */
    protected double snappedRightInset() {
        return control.snappedRightInset();
    }

    /**
     * If {@code getSkinnable().isSnapToPixel()} is false, this method
     * returns the same value, else it tries to return a value rounded to
     * the nearest pixel, but since there is no indication if the value is
     * a vertical or horizontal measurement then it may be snapped to the
     * wrong pixel size metric on screens with different horizontal and
     * vertical scales.
     * @param value the space value to be snapped
     * @return value rounded to nearest pixel
     * @deprecated replaced by {@code snapSpaceX()} and {@code snapSpaceY()}
     */
    @Deprecated(since="9")
    protected double snapSpace(double value) {
        return control.snapSpaceX(value);
    }

    /**
     * Convenience method for accessing the
     * {@link Region#snapSpaceX(double) snapSpaceX()}
     * method on the skinnable.
     * It is equivalent to calling
     * {@code getSkinnable().snapSpaceX(value)}.
     * @param value the space value to be snapped
     * @return value rounded to nearest pixel
     * @see Region#snapSpaceX(double)
     * @since 9
     */
    protected double snapSpaceX(double value) {
        return control.snapSpaceX(value);
    }

    /**
     * Convenience method for accessing the
     * {@link Region#snapSpaceY(double) snapSpaceY()}
     * method on the skinnable.
     * It is equivalent to calling
     * {@code getSkinnable().snapSpaceY(value)}.
     * @param value the space value to be snapped
     * @return value rounded to nearest pixel
     * @see Region#snapSpaceY(double)
     * @since 9
     */
    protected double snapSpaceY(double value) {
        return control.snapSpaceY(value);
    }

    /**
     * If {@code getSkinnable().isSnapToPixel()} is false, this method
     * returns the same value, else it tries to return a value ceiled to
     * the nearest pixel, but since there is no indication if the value is
     * a vertical or horizontal measurement then it may be snapped to the
     * wrong pixel size metric on screens with different horizontal and
     * vertical scales.
     * @param value the size value to be snapped
     * @return value ceiled to nearest pixel
     * @deprecated replaced by {@code snapSizeX()} and {@code snapSizeY()}
     */
    @Deprecated(since="9")
    protected double snapSize(double value) {
        return control.snapSizeX(value);
    }

    /**
     * Convenience method for accessing the
     * {@link Region#snapSizeX(double) snapSizeX()}
     * method on the skinnable.
     * It is equivalent to calling
     * {@code getSkinnable().snapSizeX(value)}.
     * @param value the size value to be snapped
     * @return value ceiled to nearest pixel
     * @see Region#snapSizeX(double)
     * @since 9
     */
    protected double snapSizeX(double value) {
        return control.snapSizeX(value);
    }

    /**
     * Convenience method for accessing the
     * {@link Region#snapSizeY(double) snapSizeY()}
     * method on the skinnable.
     * It is equivalent to calling
     * {@code getSkinnable().snapSizeY(value)}.
     * @param value the size value to be snapped
     * @return value ceiled to nearest pixel
     * @see Region#snapSizeY(double)
     * @since 9
     */
    protected double snapSizeY(double value) {
        return control.snapSizeY(value);
    }

    /**
     * If {@code getSkinnable().isSnapToPixel()} is false, this method
     * returns the same value, else it tries to return a value rounded to
     * the nearest pixel, but since there is no indication if the value is
     * a vertical or horizontal measurement then it may be snapped to the
     * wrong pixel size metric on screens with different horizontal and
     * vertical scales.
     * @param value the position value to be snapped
     * @return value rounded to nearest pixel
     * @deprecated replaced by {@code snapPositionX()} and {@code snapPositionY()}
     */
    @Deprecated(since="9")
    protected double snapPosition(double value) {
        return control.snapPositionX(value);
    }

    /**
     * Convenience method for accessing the
     * {@link Region#snapPositionX(double) snapPositionX()}
     * method on the skinnable.
     * It is equivalent to calling
     * {@code getSkinnable().snapPositionX(value)}.
     * @param value the position value to be snapped
     * @return value rounded to nearest pixel
     * @see Region#snapPositionX(double)
     * @since 9
     */
    protected double snapPositionX(double value) {
        return control.snapPositionX(value);
    }

    /**
     * Convenience method for accessing the
     * {@link Region#snapPositionY(double) snapPositionY()}
     * method on the skinnable.
     * It is equivalent to calling
     * {@code getSkinnable().snapPositionY(value)}.
     * @param value the position value to be snapped
     * @return value rounded to nearest pixel
     * @see Region#snapPositionY(double)
     * @since 9
     */
    protected double snapPositionY(double value) {
        return control.snapPositionY(value);
    }

    /**
     * Utility method which positions the child within an area of this
     * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
     * with a baseline offset relative to that area.
     * 

* This function does not resize the node and uses the node's layout bounds * width and height to determine how it should be positioned within the area. *

* If the vertical alignment is {@code VPos.BASELINE} then it * will position the node so that its own baseline aligns with the passed in * {@code baselineOffset}, otherwise the baseline parameter is ignored. *

* If {@code snapToPixel} is {@code true} for this skin, then the x/y position * values will be rounded to their nearest pixel boundaries. * * @param child the child being positioned within this skin * @param areaX the horizontal offset of the layout area relative to this skin * @param areaY the vertical offset of the layout area relative to this skin * @param areaWidth the width of the layout area * @param areaHeight the height of the layout area * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE * @param halignment the horizontal alignment for the child within the area * @param valignment the vertical alignment for the child within the area * */ protected void positionInArea(Node child, double areaX, double areaY, double areaWidth, double areaHeight, double areaBaselineOffset, HPos halignment, VPos valignment) { positionInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, Insets.EMPTY, halignment, valignment); } /** * Utility method which positions the child within an area of this * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, * with a baseline offset relative to that area. *

* This function does not resize the node and uses the node's layout bounds * width and height to determine how it should be positioned within the area. *

* If the vertical alignment is {@code VPos.BASELINE} then it * will position the node so that its own baseline aligns with the passed in * {@code baselineOffset}, otherwise the baseline parameter is ignored. *

* If {@code snapToPixel} is {@code true} for this skin, then the x/y position * values will be rounded to their nearest pixel boundaries. *

* If {@code margin} is non-null, then that space will be allocated around the * child within the layout area. margin may be null. * * @param child the child being positioned within this skin * @param areaX the horizontal offset of the layout area relative to this skin * @param areaY the vertical offset of the layout area relative to this skin * @param areaWidth the width of the layout area * @param areaHeight the height of the layout area * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE * @param margin the margin of space to be allocated around the child * @param halignment the horizontal alignment for the child within the area * @param valignment the vertical alignment for the child within the area * * @since JavaFX 8.0 */ protected void positionInArea(Node child, double areaX, double areaY, double areaWidth, double areaHeight, double areaBaselineOffset, Insets margin, HPos halignment, VPos valignment) { Region.positionInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, margin, halignment, valignment, control.isSnapToPixel()); } /** * Utility method which lays out the child within an area of this * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, * with a baseline offset relative to that area. *

* If the child is resizable, this method will resize it to fill the specified * area unless the node's maximum size prevents it. If the node's maximum * size preference is less than the area size, the maximum size will be used. * If node's maximum is greater than the area size, then the node will be * resized to fit within the area, unless its minimum size prevents it. *

* If the child has a non-null contentBias, then this method will use it when * resizing the child. If the contentBias is horizontal, it will set its width * first to the area's width (up to the child's max width limit) and then pass * that value to compute the child's height. If child's contentBias is vertical, * then it will set its height to the area height (up to child's max height limit) * and pass that height to compute the child's width. If the child's contentBias * is null, then it's width and height have no dependencies on each other. *

* If the child is not resizable (Shape, Group, etc) then it will only be * positioned and not resized. *

* If the child's resulting size differs from the area's size (either * because it was not resizable or it's sizing preferences prevented it), then * this function will align the node relative to the area using horizontal and * vertical alignment values. * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned * with the area baseline offset parameter, otherwise the baseline parameter * is ignored. *

* If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y * values will be rounded to their nearest pixel boundaries and the * width/height values will be ceiled to the next pixel boundary. * * @param child the child being positioned within this skin * @param areaX the horizontal offset of the layout area relative to this skin * @param areaY the vertical offset of the layout area relative to this skin * @param areaWidth the width of the layout area * @param areaHeight the height of the layout area * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE * @param halignment the horizontal alignment for the child within the area * @param valignment the vertical alignment for the child within the area * */ protected void layoutInArea(Node child, double areaX, double areaY, double areaWidth, double areaHeight, double areaBaselineOffset, HPos halignment, VPos valignment) { layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, Insets.EMPTY, true, true, halignment, valignment); } /** * Utility method which lays out the child within an area of this * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, * with a baseline offset relative to that area. *

* If the child is resizable, this method will resize it to fill the specified * area unless the node's maximum size prevents it. If the node's maximum * size preference is less than the area size, the maximum size will be used. * If node's maximum is greater than the area size, then the node will be * resized to fit within the area, unless its minimum size prevents it. *

* If the child has a non-null contentBias, then this method will use it when * resizing the child. If the contentBias is horizontal, it will set its width * first to the area's width (up to the child's max width limit) and then pass * that value to compute the child's height. If child's contentBias is vertical, * then it will set its height to the area height (up to child's max height limit) * and pass that height to compute the child's width. If the child's contentBias * is null, then it's width and height have no dependencies on each other. *

* If the child is not resizable (Shape, Group, etc) then it will only be * positioned and not resized. *

* If the child's resulting size differs from the area's size (either * because it was not resizable or it's sizing preferences prevented it), then * this function will align the node relative to the area using horizontal and * vertical alignment values. * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned * with the area baseline offset parameter, otherwise the baseline parameter * is ignored. *

* If {@code margin} is non-null, then that space will be allocated around the * child within the layout area. margin may be null. *

* If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y * values will be rounded to their nearest pixel boundaries and the * width/height values will be ceiled to the next pixel boundary. * * @param child the child being positioned within this skin * @param areaX the horizontal offset of the layout area relative to this skin * @param areaY the vertical offset of the layout area relative to this skin * @param areaWidth the width of the layout area * @param areaHeight the height of the layout area * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE * @param margin the margin of space to be allocated around the child * @param halignment the horizontal alignment for the child within the area * @param valignment the vertical alignment for the child within the area */ protected void layoutInArea(Node child, double areaX, double areaY, double areaWidth, double areaHeight, double areaBaselineOffset, Insets margin, HPos halignment, VPos valignment) { layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, margin, true, true, halignment, valignment); } /** * Utility method which lays out the child within an area of this * skin defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight}, * with a baseline offset relative to that area. *

* If the child is resizable, this method will use {@code fillWidth} and {@code fillHeight} * to determine whether to resize it to fill the area or keep the child at its * preferred dimension. If fillWidth/fillHeight are true, then this method * will only resize the child up to its max size limits. If the node's maximum * size preference is less than the area size, the maximum size will be used. * If node's maximum is greater than the area size, then the node will be * resized to fit within the area, unless its minimum size prevents it. *

* If the child has a non-null contentBias, then this method will use it when * resizing the child. If the contentBias is horizontal, it will set its width * first and then pass that value to compute the child's height. If child's * contentBias is vertical, then it will set its height first * and pass that value to compute the child's width. If the child's contentBias * is null, then it's width and height have no dependencies on each other. *

* If the child is not resizable (Shape, Group, etc) then it will only be * positioned and not resized. *

* If the child's resulting size differs from the area's size (either * because it was not resizable or it's sizing preferences prevented it), then * this function will align the node relative to the area using horizontal and * vertical alignment values. * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned * with the area baseline offset parameter, otherwise the baseline parameter * is ignored. *

* If {@code margin} is non-null, then that space will be allocated around the * child within the layout area. margin may be null. *

* If {@code snapToPixel} is {@code true} for this skin, then the resulting x,y * values will be rounded to their nearest pixel boundaries and the * width/height values will be ceiled to the next pixel boundary. * * @param child the child being positioned within this skin * @param areaX the horizontal offset of the layout area relative to this skin * @param areaY the vertical offset of the layout area relative to this skin * @param areaWidth the width of the layout area * @param areaHeight the height of the layout area * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE * @param margin the margin of space to be allocated around the child * @param fillWidth whether or not the child should be resized to fill the area width or kept to its preferred width * @param fillHeight whether or not the child should e resized to fill the area height or kept to its preferred height * @param halignment the horizontal alignment for the child within the area * @param valignment the vertical alignment for the child within the area */ protected void layoutInArea(Node child, double areaX, double areaY, double areaWidth, double areaHeight, double areaBaselineOffset, Insets margin, boolean fillWidth, boolean fillHeight, HPos halignment, VPos valignment) { Region.layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, margin, fillWidth, fillHeight, halignment, valignment, control.isSnapToPixel()); } /* ************************************************************************* * * * Private Implementation * * * **************************************************************************/ /* ************************************************************************ * * * Specialization of CSS handling code * * * **************************************************************************/ private static class StyleableProperties { private static final List> STYLEABLES; static { STYLEABLES = Collections.unmodifiableList(Control.getClassCssMetaData()); } } /** * Returns the CssMetaData associated with this class, which may include the * CssMetaData of its superclasses. * @return the CssMetaData associated with this class, which may include the * CssMetaData of its superclasses */ public static List> getClassCssMetaData() { return SkinBase.StyleableProperties.STYLEABLES; } /** * This method should delegate to {@link Node#getClassCssMetaData()} so that * a Node's CssMetaData can be accessed without the need for reflection. * @return The CssMetaData associated with this node, which may include the * CssMetaData of its superclasses. */ public List> getCssMetaData() { return getClassCssMetaData(); } /** * Used to specify that a pseudo-class of this Node has changed. If the * pseudo-class is used in a CSS selector that matches this Node, CSS will * be reapplied. Typically, this method is called from the {@code invalidated} * method of a property that is used as a pseudo-class. For example: *


     *
     *     private static final PseudoClass MY_PSEUDO_CLASS_STATE = PseudoClass.getPseudoClass("my-state");
     *
     *     BooleanProperty myPseudoClassState = new BooleanPropertyBase(false) {
     *
     *           {@literal @}Override public void invalidated() {
     *                pseudoClassStateChanged(MY_PSEUDO_CLASS_STATE, get());
     *           }
     *
     *           {@literal @}Override public Object getBean() {
     *               return MyControl.this;
     *           }
     *
     *           {@literal @}Override public String getName() {
     *               return "myPseudoClassState";
     *           }
     *       };
     * 
* * @see Node#pseudoClassStateChanged * @param pseudoClass the pseudo-class that has changed state * @param active whether or not the state is active * @since JavaFX 8.0 */ public final void pseudoClassStateChanged(PseudoClass pseudoClass, boolean active) { Control ctl = getSkinnable(); if (ctl != null) { ctl.pseudoClassStateChanged(pseudoClass, active); } } /* ************************************************************************* * * * Accessibility handling * * * **************************************************************************/ /** * This method is called by the assistive technology to request * the value for an attribute. *

* This method is commonly overridden by subclasses to implement * attributes that are required for a specific role.
* If a particular attribute is not handled, the superclass implementation * must be called. *

* * @param attribute the requested attribute * @param parameters optional list of parameters * @return the value for the requested attribute * * @see AccessibleAttribute * @see Node#queryAccessibleAttribute * * @since JavaFX 8u40 */ protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { return null; } /** * This method is called by the assistive technology to request the action * indicated by the argument should be executed. *

* This method is commonly overridden by subclasses to implement * action that are required for a specific role.
* If a particular action is not handled, the superclass implementation * must be called. *

* * @param action the action to execute * @param parameters optional list of parameters * * @see AccessibleAction * @see Node#executeAccessibleAction * * @since JavaFX 8u40 */ protected void executeAccessibleAction(AccessibleAction action, Object... parameters) { } /* ************************************************************************* * * * Testing-only API * * * **************************************************************************/ }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy