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

jfxtras.internal.scene.control.skin.window.DefaultWindowSkin Maven / Gradle / Ivy

There is a newer version: 0.2.5.0
Show newest version
/*
 * Copyright 2012-2016 Michael Hoffer . All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * Please cite the following publication(s):
 *
 * M. Hoffer, C.Poliwoda, G.Wittum. Visual Reflection Library -
 * A Framework for Declarative GUI Programming on the Java Platform.
 * Computing and Visualization in Science, 2011, in press.
 *
 * THIS SOFTWARE IS PROVIDED BY Michael Hoffer  "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Michael Hoffer  OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of Michael Hoffer .
 */
package jfxtras.internal.scene.control.skin.window;

import javafx.animation.Animation.Status;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ListChangeListener.Change;
import javafx.geometry.Bounds;
import javafx.scene.CacheHint;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.SkinBase;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.util.Duration;
import jfxtras.scene.control.window.SelectableNode;
import jfxtras.scene.control.window.Window;
import jfxtras.scene.control.window.WindowIcon;
import jfxtras.scene.control.window.WindowUtil;

/**
 *
 * @author Michael Hoffer <[email protected]>
 */
public class DefaultWindowSkin extends SkinBase {

    private double mouseX;
    private double mouseY;
    private double nodeX = 0;
    private double nodeY = 0;
    private BooleanProperty draggingProperty = new SimpleBooleanProperty();
    private boolean zoomable = true;
    private double minScale = 0.1;
    private double maxScale = 10;
    private double scaleIncrement = 0.001;
    private ResizeMode resizeMode;
    private boolean resizeTop;
    private boolean resizeLeft;
    private boolean resizeBottom;
    private boolean resizeRight;
    private TitleBar titleBar;
    private Window control;
    private Pane root = new Pane();
    private double contentScale = 1.0;
    private double oldHeight;
    private Timeline minimizeTimeLine;

    public DefaultWindowSkin(Window w) {
        super(w);
        this.control = w;
        titleBar = new TitleBar(control);
        titleBar.setTitle("");
        init();
    }

    private void init() {

        titleBar.widthProperty().addListener((ov) -> {
            titleBar.setPrefWidth(titleBar.minWidth(-1));
            this.layoutChildren(0, 0, control.getWidth(), control.getHeight());
        });

        getChildren().add(root);
        root.getChildren().add(titleBar);

        for (WindowIcon i : control.getLeftIcons()) {
            titleBar.addLeftIcon(i);
        }

        for (WindowIcon i : control.getRightIcons()) {
            titleBar.addRightIcon(i);
        }

        control.getLeftIcons().addListener(
                (ListChangeListener.Change change) -> {
                    while (change.next()) {
                        // TODO handle permutation
//                        if (change.wasPermutated()) {
//                            for (int i = change.getFrom(); i < change.getTo(); ++i) {
//                                //permutate
//                            }
//                        } else if (change.wasUpdated()) {
//                            //update item
//                        } else 
                            
                        if (change.wasRemoved()) {
                            for (WindowIcon i : change.getRemoved()) {
                                titleBar.removeLeftIcon(i);
                            }
                        } else if (change.wasAdded()) {
                            for (WindowIcon i : change.getAddedSubList()) {
                                titleBar.addLeftIcon(i);
                            }
                        }
                    }
                });

        control.getRightIcons().addListener(
                (ListChangeListener.Change change) -> {
                    while (change.next()) {
                        // TODO handle permutation
//                        if (change.wasPermutated()) {
//                            for (int i = change.getFrom(); i < change.getTo(); ++i) {
//                                //permutate
//                            }
//                        } else if (change.wasUpdated()) {
//                            //update item
//                        } else 
                            
                            if (change.wasRemoved()) {
                            for (WindowIcon i : change.getRemoved()) {
                                titleBar.removeRightIcon(i);
                            }
                        } else if (change.wasAdded()) {
                            for (WindowIcon i : change.getAddedSubList()) {
                                titleBar.addRightIcon(i);
                            }
                        }
                    }
                });

        control.minimizedProperty().addListener(
                (ov, oldValue, newValue) -> {

                    boolean storeOldHeight = minimizeTimeLine == null && newValue;

                    if (minimizeTimeLine != null) {
                        minimizeTimeLine.stop();
                        minimizeTimeLine = null;
                    }

                    double newHeight;

                    if (newValue) {
                        newHeight = titleBar.getHeight();
                    } else {
                        newHeight = oldHeight;
                    }

                    if (storeOldHeight) {
                        oldHeight = control.getPrefHeight();
                    }

                    minimizeTimeLine = new Timeline(
                            new KeyFrame(Duration.ZERO,
                                    new KeyValue(control.prefHeightProperty(),
                                            control.getPrefHeight())),
                            new KeyFrame(Duration.seconds(0.1),
                                    new KeyValue(control.prefHeightProperty(),
                                            newHeight)));

                    minimizeTimeLine.statusProperty().addListener(
                            (ObservableValue ov2,
                                    Status oldStatus, Status newStatus) -> {
                                if (newStatus == Status.STOPPED) {

//                                    // restore cache hint
//                                    getSkinnable().setCache(true);
//                                    getSkinnable().setCacheHint(CacheHint.SPEED);
                                    minimizeTimeLine = null;
                                    if (newValue) {
                                        control.getContentPane().setVisible(false);
                                    }
                                }
                            });

                    // temporarily disable cache hint due to rendering bugs
//                    getSkinnable().setCache(false);
//                    getSkinnable().setCacheHint(CacheHint.DEFAULT);
                    minimizeTimeLine.play();
                });

        control.prefHeightProperty().addListener(
                new MinimizeHeightListener(control, titleBar));

        initMouseEventHandlers();

        titleBar.setTitle(control.getTitle());

        control.titleProperty().addListener(
                (ObservableValue ov,
                        String oldValue, String newValue) -> {
                    titleBar.setTitle(newValue);
                    control.requestLayout();
                });

        root.getChildren().add(control.getContentPane());

        control.getContentPane().setManaged(false);

//        InvalidationListener contentLayoutListener = (ov) -> {
//            
//            control.autosize();
//            root.autosize();
//        };
//
//        control.getContentPane().needsLayoutProperty().addListener(contentLayoutListener);
//
//        control.contentPaneProperty().addListener(
//                (ObservableValue ov,
//                        Pane oldValue, Pane newValue) -> {
//                    root.getChildren().remove(oldValue);
//                    root.getChildren().add(newValue);
//                    newValue.setManaged(false);
//                    newValue.needsLayoutProperty().
//                    addListener(contentLayoutListener);
//                });
        titleBar.setStyle(control.getStyle());

        control.styleProperty().addListener(
                (ObservableValue ov, String t, String t1) -> {
                    titleBar.setStyle(t1);
                });

        titleBar.getStyleClass().setAll(control.getTitleBarStyleClass());
        titleBar.getLabel().getStyleClass().setAll(control.getTitleBarStyleClass());

        control.titleBarStyleClassProperty().addListener(
                (ObservableValue ov, String t, String t1) -> {
                    titleBar.getStyleClass().setAll(t1);
                    titleBar.getLabel().getStyleClass().setAll(t1);
                });

        titleBar.getStylesheets().setAll(control.getStylesheets());

        control.getStylesheets().addListener(
                (Change change) -> {
                    while (change.next()) {
                        // TODO handle permutation
//                        if (change.wasPermutated()) {
//                            for (int i = change.getFrom(); i < change.getTo(); ++i) {
//                                //permutate
//                            }
//                        } else if (change.wasUpdated()) {
//                            //update item
//                        } else 
                            if (change.wasRemoved()) {
                            for (String i : change.getRemoved()) {
                                titleBar.getStylesheets().remove(i);
                            }
                        } else if (change.wasAdded()) {
                            for (String i : change.getAddedSubList()) {
                                titleBar.getStylesheets().add(i);
                            }
                        }
                    }
                });

        Border prevBorder = control.getBorder();

        control.selectedProperty().addListener(
                (ov, oldValue, newValue) -> {
                    if (newValue) {
                        control.setBorder(new Border(
                                new BorderStroke(
                                        control.getSelectionBorderColor(),
                                        BorderStrokeStyle.SOLID,
                                        new CornerRadii(3),
                                        new BorderWidths(2))));
                        if (control.isSelectionEffectEnabled()) {
                            ColorAdjust effect
                            = new ColorAdjust(-0.25, 0.2, 0.8, 0);
//                            Glow glow = new Glow(0.5);
//                            glow.setInput(effect);
                            control.setEffect(effect);
                        }
                    } else {
                        control.setBorder(prevBorder);
                        control.setEffect(null);
                    }
                });

        getSkinnable().setCacheHint(CacheHint.SPEED);

//        control.resizingProperty().addListener((ov) -> {
//            control.setCache(control.isResizing());
//        });
        // counter intuitive caching (see http://bit.ly/1MemgLz why)
//        control.resizingProperty().addListener((ov) -> {
//            control.setCache(!control.isResizing());
//        });
//
//        InvalidationListener cacheListener = (ov) -> {
//            if (!control.isResizing()) {
//                control.setCache(
//                        !control.isResizing()
//                        && !control.getParent().isCache()
//                );
//            } else {
//                control.setCache(false);
//            }
//        };
//        Parent p = control.getParent();
//        if (p != null) {
//            p.cacheProperty().addListener(cacheListener);
//        }
//        control.parentProperty().addListener((ov, oldV, newV) -> {
//            if (oldV != null) {
//                oldV.cacheProperty().removeListener(cacheListener);
//            }
//            if (newV != null) {
//                newV.cacheProperty().addListener(cacheListener);
//            }
//        });

        Rectangle clipRectangle = new Rectangle();
        control.getContentPane().setClip(clipRectangle);
        control.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
            clipRectangle.setX(2);
            clipRectangle.setY(2);
            clipRectangle.setWidth(newValue.getWidth()-1);
            clipRectangle.setHeight(newValue.getHeight()-1);
        });

    }

    private void initMouseEventHandlers() {

        getSkinnable().onMousePressedProperty().set((event) -> {

            final Node n = control;

            final double parentScaleX = n.getParent().
                    localToSceneTransformProperty().getValue().getMxx();
            final double parentScaleY = n.getParent().
                    localToSceneTransformProperty().getValue().getMyy();

            mouseX = event.getSceneX();
            mouseY = event.getSceneY();

            nodeX = n.getLayoutX() * parentScaleX;
            nodeY = n.getLayoutY() * parentScaleY;

            if (control.isMoveToFront()) {
                control.toFront();
            }

            if (control.isSelected()) {
                selectedWindowsToFront();
            }
        });

        //Event Listener for MouseDragged
        getSkinnable().onMouseDraggedProperty().set((event) -> {
            final Node n = control;

            final double parentScaleX = n.getParent().
                    localToSceneTransformProperty().getValue().getMxx();
            final double parentScaleY = n.getParent().
                    localToSceneTransformProperty().getValue().getMyy();

            final double scaleX = n.localToSceneTransformProperty().
                    getValue().getMxx();
            final double scaleY = n.localToSceneTransformProperty().
                    getValue().getMyy();

            Bounds boundsInScene
                    = control.localToScene(control.getBoundsInLocal());

            double sceneX = boundsInScene.getMinX();
            double sceneY = boundsInScene.getMinY();

            double offsetX = event.getSceneX() - mouseX;
            double offsetY = event.getSceneY() - mouseY;

            if (resizeMode == ResizeMode.NONE && control.isMovable()) {

                nodeX += offsetX;
                nodeY += offsetY;

                double scaledX = nodeX * 1 / parentScaleX;
                double scaledY = nodeY * 1 / parentScaleY;

                double offsetForAllX = scaledX - n.getLayoutX();
                double offsetForAllY = scaledY - n.getLayoutY();

                n.setLayoutX(scaledX);
                n.setLayoutY(scaledY);

                setDragging(true);

                // move all selected windows
                if (control.isSelected()) {
                    dragSelectedWindows(offsetForAllX, offsetForAllY);
                }

            } else {

                double width = n.getBoundsInLocal().getMaxX()
                        - n.getBoundsInLocal().getMinX();
                double height = n.getBoundsInLocal().getMaxY()
                        - n.getBoundsInLocal().getMinY();

                if (resizeTop) {
//                        System.out.println("TOP");

                    double insetOffset = getSkinnable().getInsets().getTop() * 0.5;

                    double yDiff
                            = sceneY / parentScaleY
                            + insetOffset
                            - event.getSceneY() / parentScaleY;

                    double newHeight = control.getPrefHeight() + yDiff;

                    if (newHeight > control.minHeight(0)) {
                        control.setLayoutY(control.getLayoutY() - yDiff);
                        control.setPrefHeight(newHeight);
                    }
                }
                if (resizeLeft) {
//                        System.out.println("LEFT");

                    double insetOffset = getSkinnable().getInsets().getLeft() * 0.5;

                    double xDiff = sceneX / parentScaleX
                            + insetOffset
                            - event.getSceneX() / parentScaleX;

                    double newWidth = control.getPrefWidth() + xDiff;

                    if (newWidth > Math.max(control.minWidth(0),
                            control.getContentPane().minWidth(0))) {
                        control.setLayoutX(control.getLayoutX() - xDiff);
                        control.setPrefWidth(newWidth);
                    }
                }

                if (resizeBottom) {
//                        System.out.println("BOTTOM");

                    double insetOffset = getSkinnable().getInsets().getBottom() * 0.5;

                    double yDiff = event.getSceneY() / parentScaleY
                            - sceneY / parentScaleY - insetOffset;

                    double newHeight = yDiff;

                    newHeight = Math.max(
                            newHeight, control.minHeight(0));

                    if (newHeight < control.maxHeight(0)) {
                        control.setPrefHeight(newHeight);
                    }
                }
                if (resizeRight) {

                    double insetOffset = getSkinnable().getInsets().getRight() * 0.5;

                    double xDiff = event.getSceneX() / parentScaleX
                            - sceneX / parentScaleY - insetOffset;

                    double newWidth = xDiff;

                    newWidth = Math.max(
                            newWidth,
                            Math.max(control.getContentPane().minWidth(0),
                                    control.minWidth(0)));

                    if (newWidth < control.maxWidth(0)) {
                        control.setPrefWidth(newWidth);
                    }
                }

//                if (RESIZE_BOTTOM || RESIZE_TOP || RESIZE_LEFT || RESIZE_RIGHT) {
//                    getSkinnable().setCache(false);
//
//                } else {
//                    getSkinnable().setCache(true);
//                    getSkinnable().setCacheHint(CacheHint.DEFAULT);
//                }
            }

            mouseX = event.getSceneX();
            mouseY = event.getSceneY();
        });

        getSkinnable().onMouseClickedProperty().set((MouseEvent event) -> {
            setDragging(false);
        });

        getSkinnable().onMouseMovedProperty().set((MouseEvent t) -> {

            if (control.isMinimized() || !control.isResizableWindow()) {

                resizeTop = false;
                resizeLeft = false;
                resizeBottom = false;
                resizeRight = false;

                resizeMode = ResizeMode.NONE;

                return;
            }

            final Node n = control;

            final double parentScaleX = n.getParent().
                    localToSceneTransformProperty().getValue().getMxx();
            final double parentScaleY = n.getParent().
                    localToSceneTransformProperty().getValue().getMyy();

            final double scaleX = n.localToSceneTransformProperty().getValue().getMxx();
            final double scaleY = n.localToSceneTransformProperty().getValue().getMyy();

            final double border = control.getResizableBorderWidth() * scaleX;

            double diffMinX = Math.abs(n.getLayoutBounds().getMinX() - t.getX() + getSkinnable().getInsets().getLeft());
            double diffMinY = Math.abs(n.getLayoutBounds().getMinY() - t.getY() + getSkinnable().getInsets().getTop());
            double diffMaxX = Math.abs(n.getLayoutBounds().getMaxX() - t.getX() - getSkinnable().getInsets().getRight());
            double diffMaxY = Math.abs(n.getLayoutBounds().getMaxY() - t.getY() - getSkinnable().getInsets().getBottom());

            boolean left = diffMinX * scaleX < Math.max(border, getSkinnable().getInsets().getLeft() / 2 * scaleX);
            boolean top = diffMinY * scaleY < Math.max(border, getSkinnable().getInsets().getTop() / 2 * scaleY);
            boolean right = diffMaxX * scaleX < Math.max(border, getSkinnable().getInsets().getRight() / 2 * scaleX);
            boolean bottom = diffMaxY * scaleY < Math.max(border, getSkinnable().getInsets().getBottom() / 2 * scaleY);

            resizeTop = false;
            resizeLeft = false;
            resizeBottom = false;
            resizeRight = false;

            if (left && !top && !bottom) {
                n.setCursor(Cursor.W_RESIZE);
                resizeMode = ResizeMode.LEFT;
                resizeLeft = true;
            } else if (left && top && !bottom) {
                n.setCursor(Cursor.NW_RESIZE);
                resizeMode = ResizeMode.TOP_LEFT;
                resizeLeft = true;
                resizeTop = true;
            } else if (left && !top && bottom) {
                n.setCursor(Cursor.SW_RESIZE);
                resizeMode = ResizeMode.BOTTOM_LEFT;
                resizeLeft = true;
                resizeBottom = true;
            } else if (right && !top && !bottom) {
                n.setCursor(Cursor.E_RESIZE);
                resizeMode = ResizeMode.RIGHT;
                resizeRight = true;
            } else if (right && top && !bottom) {
                n.setCursor(Cursor.NE_RESIZE);
                resizeMode = ResizeMode.TOP_RIGHT;
                resizeRight = true;
                resizeTop = true;
            } else if (right && !top && bottom) {
                n.setCursor(Cursor.SE_RESIZE);
                resizeMode = ResizeMode.BOTTOM_RIGHT;
                resizeRight = true;
                resizeBottom = true;
            } else if (top && !left && !right) {
                n.setCursor(Cursor.N_RESIZE);
                resizeMode = ResizeMode.TOP;
                resizeTop = true;
            } else if (bottom && !left && !right) {
                n.setCursor(Cursor.S_RESIZE);
                resizeMode = ResizeMode.BOTTOM;
                resizeBottom = true;
            } else {
                n.setCursor(Cursor.DEFAULT);
                resizeMode = ResizeMode.NONE;
            }

            control.autosize();
        });
    }

    /**
     * @return the zoomable
     */
    public boolean isZoomable() {
        return zoomable;
    }

    /**
     * @param zoomable the zoomable to set
     */
    public void setZoomable(boolean zoomable) {
        this.zoomable = zoomable;
    }

    /**
     * @return the dragging
     */
    protected boolean isDragging() {
        return draggingProperty().get();
    }

    public void removeNode(Node n) {
        getChildren().remove(n);
    }

    /**
     * @return the minScale
     */
    public double getMinScale() {
        return minScale;
    }

    /**
     * @param minScale the minScale to set
     */
    public void setMinScale(double minScale) {
        this.minScale = minScale;
    }

    /**
     * @return the maxScale
     */
    public double getMaxScale() {
        return maxScale;
    }

    /**
     * @param maxScale the maxScale to set
     */
    public void setMaxScale(double maxScale) {
        this.maxScale = maxScale;
    }

    /**
     * @return the scaleIncrement
     */
    public double getScaleIncrement() {
        return scaleIncrement;
    }

    /**
     * @param scaleIncrement the scaleIncrement to set
     */
    public void setScaleIncrement(double scaleIncrement) {
        this.scaleIncrement = scaleIncrement;
    }

    @Override
    protected void layoutChildren(double x, double y, double w, double h) {

        super.layoutChildren(x, y, w, h);

        root.relocate(0, 0);
        root.resize(root.getWidth()
                + getSkinnable().getInsets().getLeft()
                + getSkinnable().getInsets().getRight(),
                root.getHeight()
                + getSkinnable().getInsets().getTop()
                + getSkinnable().getInsets().getBottom());

        titleBar.relocate(0, 0);

        double titleBarWidth = titleBar.computeMinWidth(-1);

        double windowWidth = root.getWidth();

        titleBar.resize(windowWidth
                - getSkinnable().getInsets().getLeft()
                - getSkinnable().getInsets().getRight(),
                titleBar.prefHeight(-1));

        if (titleBarWidth > root.getWidth()) {
            control.setPrefWidth(titleBarWidth
                    + getSkinnable().getInsets().getLeft()
                    + getSkinnable().getInsets().getRight());

            control.autosize();
            control.layout();

            root.autosize();
            root.layout();

            titleBar.autosize();
            titleBar.layout();
        }
        titleBar.layoutChildren();

        double leftAndRight = getSkinnable().getInsets().getLeft()
                + getSkinnable().getInsets().getRight();
        double topAndBottom = getSkinnable().getInsets().getTop()
                + getSkinnable().getInsets().getBottom();

        control.getContentPane().relocate(
                getSkinnable().getInsets().getLeft(),
                titleBar.prefHeight(-1));

        double rootW = root.getWidth(); //Math.max(root.getWidth(), root.getMinWidth());
        double rootH = root.getHeight();//Math.max(root.getHeight(), root.getMinHeight());

        double contentWidth = rootW - leftAndRight;
        double contentHeight = rootH - getSkinnable().getInsets().
                getBottom() - titleBar.prefHeight(-1);

        control.getContentPane().resize(
                contentWidth,
                contentHeight);

    }

    @Override
    protected double computeMinWidth(double width, double topInset,
            double rightInset, double bottomInset, double leftInset) {

        double result = root.minWidth(width);

        double minWidth = Math.max(titleBar.minWidth(width),
                control.getContentPane().minWidth(width)
                + getSkinnable().getInsets().getLeft()
                + getSkinnable().getInsets().getRight());

        result = Math.max(result, minWidth);

        return result;
    }

    public double computeMinWidth() {
        double result = root.minWidth(-1);

        double minWidth = Math.max(titleBar.minWidth(-1),
                control.getContentPane().minWidth(-1)
                + getSkinnable().getInsets().getLeft()
                + getSkinnable().getInsets().getRight());

        result = Math.max(result, minWidth);

        return result;
    }

    @Override
    protected double computePrefWidth(double width, double topInset,
            double rightInset, double bottomInset, double leftInset) {

        double result = computeMinWidth(
                width, topInset, rightInset, bottomInset, leftInset);

        return result;
    }

    @Override
    protected double computeMinHeight(double height, double topInset,
            double rightInset, double bottomInset, double leftInset) {

        double result = root.minHeight(height);

        double minHeight = titleBar.prefHeight(height);

        if (!control.isMinimized() && control.getContentPane().isVisible()) {
            minHeight += control.getContentPane().minHeight(height)
                    + getSkinnable().getInsets().getBottom();
        }

        result = Math.max(result, minHeight);

        return result;
    }

    private void setDragging(boolean b) {
        draggingProperty().set(b);
    }

    private BooleanProperty draggingProperty() {
        return draggingProperty;
    }

    // TODO move from skin to behavior class (a lot of other stuff here too)
    private void dragSelectedWindows(double offsetForAllX, double offsetForAllY) {
        for (SelectableNode sN : WindowUtil.
                getDefaultClipboard().getSelectedItems()) {

            if (sN == control
                    || !(sN instanceof Window)) {
                continue;
            }

            Window selectedWindow = (Window) sN;

            if (control.getParent().
                    equals(selectedWindow.getParent())) {

                selectedWindow.setLayoutX(
                        selectedWindow.getLayoutX()
                        + offsetForAllX);
                selectedWindow.setLayoutY(
                        selectedWindow.getLayoutY()
                        + offsetForAllY);
            }
        } // end for sN
    }

    // TODO move from skin to behavior class (a lot of other stuff here too)
    private void selectedWindowsToFront() {
        for (SelectableNode sN : WindowUtil.
                getDefaultClipboard().getSelectedItems()) {

            if (sN == control
                    || !(sN instanceof Window)) {
                continue;
            }

            Window selectedWindow = (Window) sN;

            if (control.getParent().
                    equals(selectedWindow.getParent())
                    && selectedWindow.isMoveToFront()) {

                selectedWindow.toFront();
            }
        } // end for sN
    }

    static class MinimizeHeightListener implements ChangeListener {

        private final Window control;
        private final TitleBar titleBar;

        public MinimizeHeightListener(Window control, TitleBar titleBar) {
            this.control = control;
            this.titleBar = titleBar;
        }

        @Override
        public void changed(ObservableValue ov, Number t, Number t1) {
            if (control.isMinimized()
                    && control.getPrefHeight()
                    < titleBar.minHeight(0) + control.getContentPane().minHeight(0)) {
                control.getContentPane().setVisible(false);
//                System.out.println("v: false");
            } else if (!control.isMinimized()
                    && control.getPrefHeight()
                    >= titleBar.minHeight(0) + control.getContentPane().minHeight(0)) {
                control.getContentPane().setVisible(true);
//                System.out.println("v: true");
            }
        }
    }
}

class TitleBar extends HBox {

    public static final String DEFAULT_STYLE_CLASS = "window-titlebar";
    private final Pane leftIconPane;
    private final Pane rightIconPane;
    private final Text label = new Text();
    private final double iconSpacing = 3;
    Window control;
    // estimated size of "...",
    // is there a way to find out text dimension without rendering it
    private final double offset = 40;
    private double originalTitleWidth;

    private float labelWidth;

    public TitleBar(Window w) {

        this.control = w;

        setManaged(false);

        getStylesheets().setAll(w.getStylesheets());
        getStyleClass().setAll(DEFAULT_STYLE_CLASS);

        setSpacing(8);

//        label.setTextAlignment(TextAlignment.CENTER);
//        label.getStyleClass().setAll(DEFAULT_STYLE_CLASS);
        leftIconPane = new IconPane();
        rightIconPane = new IconPane();

        getChildren().add(leftIconPane);
//        getChildren().add(VFXLayoutUtil.createHBoxFiller());
        getChildren().add(label);
//        getChildren().add(VFXLayoutUtil.createHBoxFiller());
        getChildren().add(rightIconPane);

        control.boundsInParentProperty().addListener(
                (ObservableValue ov, Bounds t, Bounds t1) -> {
                    if (control.getTitle() == null
                    || getLabel().getText() == null
                    || getLabel().getText().isEmpty()) {
                        return;
                    }

                    double maxIconWidth = Math.max(
                            leftIconPane.getWidth(), rightIconPane.getWidth());

//                    if (!control.getTitle().equals(getLabel().getText())) {
//                        if (originalTitleWidth
//                        + maxIconWidth * 2 + offset < getWidth()) {
                    getLabel().setText(control.getTitle());
//                        }
//                    } else if (!"...".equals(getLabel().getText())) {
//                        if (originalTitleWidth
//                        + maxIconWidth * 2 + offset >= getWidth()) {
//                            getLabel().setText("...");
//                        }
//                    }
                });

    }

    public void setTitle(String title) {
        getLabel().setText(title);

        originalTitleWidth = getLabel().getBoundsInParent().getWidth();

        double maxIconWidth = Math.max(
                leftIconPane.getWidth(), rightIconPane.getWidth());

        if (originalTitleWidth
                + maxIconWidth * 2 + offset >= getWidth()) {
            getLabel().setText("...");
        }

        // TODO replace with official API
        labelWidth = com.sun.javafx.tk.Toolkit.getToolkit().getFontLoader().
                computeStringWidth(title, label.getFont());

        requestLayout();
        requestParentLayout();
    }

    public String getTitle() {
        return getLabel().getText();
    }

    public void addLeftIcon(Node n) {
        leftIconPane.getChildren().add(n);
        requestLayout();
        requestParentLayout();
    }

    public void addRightIcon(Node n) {
        rightIconPane.getChildren().add(n);
        requestLayout();
        requestParentLayout();
    }

    public void removeLeftIcon(Node n) {
        leftIconPane.getChildren().remove(n);
        requestLayout();
        requestParentLayout();
    }

    public void removeRightIcon(Node n) {
        rightIconPane.getChildren().remove(n);
        requestLayout();
        requestParentLayout();
    }

    @Override
    public double computeMinWidth(double h) {
        double result = super.computeMinWidth(h);

        double iconWidth
                = Math.max(
                        leftIconPane.prefWidth(h),
                        rightIconPane.prefWidth(h)) * 2;

        result = Math.max(result,
                iconWidth
                + labelWidth
                + getInsets().getLeft()
                + getInsets().getRight());

        return result + iconSpacing * 2 + offset;
    }

    @Override
    protected double computePrefWidth(double h) {
        return computeMinWidth(h);
    }

    @Override
    protected void layoutChildren() {
        super.layoutChildren();

        leftIconPane.resizeRelocate(getInsets().getLeft(), getInsets().getTop(),
                leftIconPane.prefWidth(USE_PREF_SIZE),
                getHeight() - getInsets().getTop() - getInsets().getBottom());

        rightIconPane.resize(rightIconPane.prefWidth(USE_PREF_SIZE),
                getHeight() - getInsets().getTop() - getInsets().getBottom());
        rightIconPane.relocate(getWidth() - rightIconPane.getWidth() - getInsets().getRight(),
                getInsets().getTop());
    }

    /**
     * @return the label
     */
    public final Text getLabel() {
        return label;
    }

    private static class IconPane extends Pane {

        private final double spacing = 2;

        public IconPane() {
            setManaged(false);
            //
            setPrefWidth(USE_COMPUTED_SIZE);
            setMinWidth(USE_COMPUTED_SIZE);
        }

        @Override
        protected void layoutChildren() {

            int count = 0;

            double width = getHeight();
            double height = getHeight();

            for (Node n : getManagedChildren()) {

                double x = (width + spacing) * count;

                n.resizeRelocate(x, 0, width, height);

                count++;
            }
        }

        @Override
        protected double computeMinWidth(double h) {
            return getHeight() * getChildren().size()
                    + spacing * (getChildren().size() - 1);
        }

        @Override
        protected double computeMaxWidth(double h) {
            return computeMinWidth(h);
        }

        @Override
        protected double computePrefWidth(double h) {
            return computeMinWidth(h);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy