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

com.jfoenix.controls.pannable.gestures.PanningGestures Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.jfoenix.controls.pannable.gestures;

import com.jfoenix.controls.pannable.base.IPannablePane;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;

/**
 * Add panning gestures to {@link IPannablePane} region
 *
 * @author Shadi Shaheen
 * @version 1.0
 * @since 2020-04-30
 */
public class PanningGestures {

    /**
     * Drag context used to store mouse drag data
     */
    private static class DragContext {
        double mouseAnchorX;
        double mouseAnchorY;
        double translateAnchorX;
        double translateAnchorY;
    }

    private T canvas;
    private final DragContext sceneDragContext = new DragContext();

    public PanningGestures(T canvas) {
        this.canvas = canvas;
    }

    /***************************************************************************
     *                                                                         *
     * Panning control properties                                              *
     *                                                                         *
     **************************************************************************/

    private final DoubleProperty minScaleProperty = new SimpleDoubleProperty(0.1d);

    public double getMinScale() {
        return minScaleProperty.get();
    }

    public void setMinScale(double minScale) {
        minScaleProperty.set(minScale);
    }

    public DoubleProperty minScaleProperty() {
        return minScaleProperty;
    }

    private final DoubleProperty maxScaleProperty = new SimpleDoubleProperty(10.0d);

    public double getMaxScale() {
        return maxScaleProperty.get();
    }

    public DoubleProperty maxScaleProperty() {
        return maxScaleProperty;
    }

    public void setMaxScale(double maxScale) {
        maxScaleProperty.set(maxScale);
    }

    private final DoubleProperty zoomSpeedProperty = new SimpleDoubleProperty(1.2d);

    public double getZoomSpeed() {
        return zoomSpeedProperty.get();
    }

    public DoubleProperty zoomSpeedProperty() {
        return zoomSpeedProperty;
    }

    public void setZoomSpeed(double zoomSpeed) {
        zoomSpeedProperty.set(zoomSpeed);
    }

    private SimpleBooleanProperty useViewportGestures = null;

    public final BooleanProperty useViewPortGesturesProperty() {
        return useViewportGestures;
    }

    // set true to bind panning to canvas size
    private boolean bound = false;

    public boolean isBound() {
        return bound;
    }

    public void setBound(boolean bound) {
        this.bound = bound;
    }

    /***************************************************************************
     *                                                                         *
     * Panning listeners                                                       *
     *                                                                         *
     **************************************************************************/

    private final EventHandler onMousePressedEventHandler = event -> {
        // right mouse button => panning
        if (!event.isSecondaryButtonDown()) {
            return;
        }
        sceneDragContext.mouseAnchorX = event.getSceneX();
        sceneDragContext.mouseAnchorY = event.getSceneY();
        sceneDragContext.translateAnchorX = canvas.getTranslateX();
        sceneDragContext.translateAnchorY = canvas.getTranslateY();
    };

    private final EventHandler onMouseDraggedEventHandler = event -> {
        // right mouse button => panning
        if (!event.isSecondaryButtonDown()) {
            return;
        }
        final Bounds parentLayoutBounds = canvas.getParent().getLayoutBounds();
        final Bounds boundsInParent = canvas.getBoundsInParent();
        // compute new trans X
        double newTransX = sceneDragContext.translateAnchorX + event.getSceneX() - sceneDragContext.mouseAnchorX;
        canvas.setTranslateX(bound(newTransX, parentLayoutBounds.getWidth(), boundsInParent.getWidth()));
        // compute new trans Y
        double newTransY = sceneDragContext.translateAnchorY + event.getSceneY() - sceneDragContext.mouseAnchorY;
        canvas.setTranslateY(bound(newTransY, parentLayoutBounds.getHeight(), boundsInParent.getHeight()));
        event.consume();
    };


    private double bound(double newTrans, double parentSize, double canvasSize) {
        if (!bound) {
            return newTrans;
        }
        double lowerBound = parentSize - canvasSize - 20 * canvas.getScale();
        if (parentSize > canvasSize) {
            newTrans = 1;
        } else {
            newTrans = inBound(lowerBound, 1, newTrans);
        }
        return newTrans;
    }

    private double inBound(double lowerBound, double upperBound, double val) {
        if (val < lowerBound) {
            val = lowerBound;
        } else if (val > upperBound) {
            val = upperBound;
        }
        return val;
    }

    /**
     * Zoom handler: responsible for zooming to a pivot coordinate
     */
    private final EventHandler onScrollEventHandler = event -> {
        double scale = canvas.getScale(); // currently we only use Y, same value is used for X
        if (event.getDeltaY() < 0) {
            scale /= getZoomSpeed();
        } else {
            scale *= getZoomSpeed();
        }
        scale = clamp(scale, minScaleProperty.get(), maxScaleProperty.get());
        Point2D currentPoint = canvas.parentToLocal(event.getX(), event.getY());
        canvas.setScale(scale);
        Point2D newPoint = canvas.localToParent(currentPoint);

        final Bounds parentLayoutBounds = canvas.getParent().getLayoutBounds();
        final Bounds boundsInParent = canvas.getBoundsInParent();
        double lowerBound = parentLayoutBounds.getWidth() - boundsInParent.getWidth() - 20;
        canvas.setTranslateX(bound(canvas.getTranslateX() - (newPoint.getX() - event.getX()),
            parentLayoutBounds.getWidth(), boundsInParent.getWidth()));
        canvas.setTranslateY(bound(canvas.getTranslateY() - (newPoint.getY() - event.getY()),
            parentLayoutBounds.getHeight(), boundsInParent.getHeight()));
        event.consume();
    };

    public static double clamp(double value, double min, double max) {
        if (Double.compare(value, min) < 0) {
            return min;
        }
        if (Double.compare(value, max) > 0) {
            return max;
        }
        return value;
    }

    /***************************************************************************
     *                                                                         *
     * Attach methods                                                          *
     *                                                                         *
     **************************************************************************/

    public static  PanningGestures attachViewPortGestures(T pannableCanvas) {
        return attachViewPortGestures(pannableCanvas, false);
    }

    public static  PanningGestures attachViewPortGestures(T pannableCanvas, boolean configurable) {
        PanningGestures panningGestures = new PanningGestures<>(pannableCanvas);
        if (configurable) {
            panningGestures.useViewportGestures = new SimpleBooleanProperty(true);
            panningGestures.useViewportGestures.addListener((o, oldVal, newVal) -> {
                final Parent parent = pannableCanvas.parentProperty().get();
                if (parent == null) {
                    return;
                }
                if (newVal) {
                    parent.addEventHandler(MouseEvent.MOUSE_PRESSED, panningGestures.onMousePressedEventHandler);
                    parent.addEventHandler(MouseEvent.MOUSE_DRAGGED, panningGestures.onMouseDraggedEventHandler);
                    parent.addEventHandler(ScrollEvent.ANY, panningGestures.onScrollEventHandler);
                } else {
                    parent.removeEventHandler(MouseEvent.MOUSE_PRESSED, panningGestures.onMousePressedEventHandler);
                    parent.removeEventHandler(MouseEvent.MOUSE_DRAGGED, panningGestures.onMouseDraggedEventHandler);
                    parent.removeEventHandler(ScrollEvent.ANY, panningGestures.onScrollEventHandler);
                }
            });
        }
        pannableCanvas.parentProperty().addListener((o, oldVal, newVal) -> {
            if (oldVal != null) {
                oldVal.removeEventHandler(MouseEvent.MOUSE_PRESSED, panningGestures.onMousePressedEventHandler);
                oldVal.removeEventHandler(MouseEvent.MOUSE_DRAGGED, panningGestures.onMouseDraggedEventHandler);
                oldVal.removeEventHandler(ScrollEvent.ANY, panningGestures.onScrollEventHandler);
            }
            if (newVal != null) {
                newVal.addEventHandler(MouseEvent.MOUSE_PRESSED, panningGestures.onMousePressedEventHandler);
                newVal.addEventHandler(MouseEvent.MOUSE_DRAGGED, panningGestures.onMouseDraggedEventHandler);
                newVal.addEventHandler(ScrollEvent.ANY, panningGestures.onScrollEventHandler);
            }
        });
        return panningGestures;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy