All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.jfoenix.controls.pannable.gestures.PanningGestures Maven / Gradle / Ivy
/*
* 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;
}
}