mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Remove controlsfx dependency
The controlsfx is a huge JavaFX components library (0.83MB as JAR). Turns out, we only use a single component, the PopOver. Fortunately, the PopOver is self-contained and easy to pull out from controlsfx. This commit embeds the PopOver in the project and removes controlsfx dependency. The embedded version is exactly the same as previously used in the project, so no change in semantics should occur.
This commit is contained in:
parent
19a1910bba
commit
2d4abbe143
@ -33,7 +33,6 @@ configure(subprojects) {
|
||||
bitcoinjVersion = 'a88d36d'
|
||||
btcdCli4jVersion = '975ff5d4'
|
||||
codecVersion = '1.9'
|
||||
controlsfxVersion = '8.0.6_20'
|
||||
easybindVersion = '1.0.3'
|
||||
easyVersion = '4.0.1'
|
||||
findbugsVersion = '3.0.2'
|
||||
@ -288,7 +287,6 @@ configure(project(':desktop')) {
|
||||
|
||||
dependencies {
|
||||
compile project(':core')
|
||||
compile "org.controlsfx:controlsfx:$controlsfxVersion"
|
||||
compile "net.glxn:qrgen:$qrgenVersion"
|
||||
compile "de.jensd:fontawesomefx:$fontawesomefxVersion"
|
||||
compile "de.jensd:fontawesomefx-commons:$fontawesomefxCommonsVersion"
|
||||
|
@ -18,12 +18,11 @@
|
||||
package bisq.desktop.components;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.desktop.components.controlsfx.control.PopOver;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
import org.controlsfx.control.PopOver;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TableColumn;
|
||||
|
@ -18,12 +18,11 @@
|
||||
package bisq.desktop.components;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.desktop.components.controlsfx.control.PopOver;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import de.jensd.fx.glyphs.GlyphIcons;
|
||||
|
||||
import org.controlsfx.control.PopOver;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.control.Label;
|
||||
|
@ -18,11 +18,10 @@
|
||||
package bisq.desktop.components;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.desktop.components.controlsfx.control.PopOver;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
import org.controlsfx.control.PopOver;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
|
@ -18,13 +18,12 @@
|
||||
package bisq.desktop.components;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.desktop.components.controlsfx.control.PopOver;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
|
||||
import org.controlsfx.control.PopOver;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
|
@ -40,8 +40,7 @@ import javafx.beans.property.SimpleObjectProperty;
|
||||
* There can be only 1 errorMessageDisplays at a time we use static field for it.
|
||||
* The position is derived from the position of the textField itself or if set from the layoutReference node.
|
||||
*/
|
||||
//TODO There are some rare situation where it behaves buggy. Needs further investigation and improvements. Also
|
||||
// consider replacement with controlsFX components.
|
||||
//TODO There are some rare situation where it behaves buggy. Needs further investigation and improvements.
|
||||
public class InputTextField extends JFXTextField {
|
||||
|
||||
private final ObjectProperty<InputValidator.ValidationResult> validationResult = new SimpleObjectProperty<>
|
||||
|
@ -0,0 +1,13 @@
|
||||
This package is a very minimal subset of the external library `controlsfx`.
|
||||
|
||||
Three files were embedded into the project to avoid having `controlsfx` as dependency.
|
||||
|
||||
This is based on version `8.0.6_20` tagged in commit 6a52afec3ef16094cda281abc80b4daa3d3bf1fd:
|
||||
|
||||
[https://github.com/controlsfx/controlsfx/commit/6a52afec3ef16094cda281abc80b4daa3d3bf1fd]
|
||||
|
||||
These specific files got raw copied (with package name adjustment):
|
||||
|
||||
[https://github.com/controlsfx/controlsfx/blob/6a52afec3ef16094cda281abc80b4daa3d3bf1fd/controlsfx/src/main/java/org/controlsfx/control/PopOver.java]
|
||||
[https://github.com/controlsfx/controlsfx/blob/6a52afec3ef16094cda281abc80b4daa3d3bf1fd/controlsfx/src/main/java/impl/org/controlsfx/skin/PopOverSkin.java]
|
||||
[https://github.com/controlsfx/controlsfx/blob/6a52afec3ef16094cda281abc80b4daa3d3bf1fd/controlsfx/src/main/resources/org/controlsfx/control/popover.css]
|
@ -0,0 +1,787 @@
|
||||
/**
|
||||
* Copyright (c) 2013, ControlsFX
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * 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.
|
||||
* * Neither the name of ControlsFX, any associated website, nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 CONTROLSFX 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.
|
||||
*/
|
||||
package bisq.desktop.components.controlsfx.control;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static javafx.scene.input.MouseEvent.MOUSE_CLICKED;
|
||||
import bisq.desktop.components.controlsfx.skin.PopOverSkin;
|
||||
import javafx.animation.FadeTransition;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.WeakInvalidationListener;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleDoubleProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.beans.value.WeakChangeListener;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Bounds;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.PopupControl;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.stage.Window;
|
||||
import javafx.stage.WindowEvent;
|
||||
import javafx.util.Duration;
|
||||
|
||||
/**
|
||||
* The PopOver control provides detailed information about an owning node in a
|
||||
* popup window. The popup window has a very lightweight appearance (no default
|
||||
* window decorations) and an arrow pointing at the owner. Due to the nature of
|
||||
* popup windows the PopOver will move around with the parent window when the
|
||||
* user drags it. <br>
|
||||
* <center> <img src="popover.png"/> </center> <br>
|
||||
* The PopOver can be detached from the owning node by dragging it away from the
|
||||
* owner. It stops displaying an arrow and starts displaying a title and a close
|
||||
* icon. <br>
|
||||
* <br>
|
||||
* <center> <img src="popover-detached.png"/> </center> <br>
|
||||
* The following image shows a popover with an accordion content node. PopOver
|
||||
* controls are automatically resizing themselves when the content node changes
|
||||
* its size.<br>
|
||||
* <br>
|
||||
* <center> <img src="popover-accordion.png"/> </center> <br>
|
||||
*/
|
||||
public class PopOver extends PopupControl {
|
||||
|
||||
private static final String DEFAULT_STYLE_CLASS = "popover"; //$NON-NLS-1$
|
||||
|
||||
private static final Duration DEFAULT_FADE_DURATION = Duration.seconds(.2);
|
||||
|
||||
private double targetX;
|
||||
|
||||
private double targetY;
|
||||
|
||||
/**
|
||||
* Creates a pop over with a label as the content node.
|
||||
*/
|
||||
public PopOver() {
|
||||
super();
|
||||
|
||||
getStyleClass().add(DEFAULT_STYLE_CLASS);
|
||||
|
||||
setAnchorLocation(AnchorLocation.WINDOW_TOP_LEFT);
|
||||
setOnHiding(new EventHandler<WindowEvent>() {
|
||||
@Override
|
||||
public void handle(WindowEvent evt) {
|
||||
setDetached(false);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Create some initial content.
|
||||
*/
|
||||
Label label = new Label("<No Content>"); //$NON-NLS-1$
|
||||
label.setPrefSize(200, 200);
|
||||
label.setPadding(new Insets(4));
|
||||
setContentNode(label);
|
||||
|
||||
ChangeListener<Object> repositionListener = new ChangeListener<Object>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Object> value,
|
||||
Object oldObject, Object newObject) {
|
||||
if (isShowing() && !isDetached()) {
|
||||
show(getOwnerNode(), targetX, targetY);
|
||||
adjustWindowLocation();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
arrowSize.addListener(repositionListener);
|
||||
cornerRadius.addListener(repositionListener);
|
||||
arrowLocation.addListener(repositionListener);
|
||||
arrowIndent.addListener(repositionListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a pop over with the given node as the content node.
|
||||
*
|
||||
* @param content The content shown by the pop over
|
||||
*/
|
||||
public PopOver(Node content) {
|
||||
this();
|
||||
|
||||
setContentNode(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new PopOverSkin(this);
|
||||
}
|
||||
|
||||
// Content support.
|
||||
|
||||
private final ObjectProperty<Node> contentNode = new SimpleObjectProperty<Node>(
|
||||
this, "contentNode") { //$NON-NLS-1$
|
||||
@Override
|
||||
public void setValue(Node node) {
|
||||
if (node == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"content node can not be null"); //$NON-NLS-1$
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the content shown by the pop over.
|
||||
*
|
||||
* @return the content node property
|
||||
*/
|
||||
public final ObjectProperty<Node> contentNodeProperty() {
|
||||
return contentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the content property
|
||||
*
|
||||
* @return the content node
|
||||
*
|
||||
* @see #contentNodeProperty()
|
||||
*/
|
||||
public final Node getContentNode() {
|
||||
return contentNodeProperty().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the content property.
|
||||
*
|
||||
* @param content
|
||||
* the new content node value
|
||||
*
|
||||
* @see #contentNodeProperty()
|
||||
*/
|
||||
public final void setContentNode(Node content) {
|
||||
contentNodeProperty().set(content);
|
||||
}
|
||||
|
||||
private InvalidationListener hideListener = new InvalidationListener() {
|
||||
@Override
|
||||
public void invalidated(Observable observable) {
|
||||
if (!isDetached()) {
|
||||
hide(Duration.ZERO);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private WeakInvalidationListener weakHideListener = new WeakInvalidationListener(
|
||||
hideListener);
|
||||
|
||||
private ChangeListener<Number> xListener = new ChangeListener<Number>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Number> value,
|
||||
Number oldX, Number newX) {
|
||||
setX(getX() + (newX.doubleValue() - oldX.doubleValue()));
|
||||
}
|
||||
};
|
||||
|
||||
private WeakChangeListener<Number> weakXListener = new WeakChangeListener<>(
|
||||
xListener);
|
||||
|
||||
private ChangeListener<Number> yListener = new ChangeListener<Number>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Number> value,
|
||||
Number oldY, Number newY) {
|
||||
setY(getY() + (newY.doubleValue() - oldY.doubleValue()));
|
||||
}
|
||||
};
|
||||
|
||||
private WeakChangeListener<Number> weakYListener = new WeakChangeListener<>(
|
||||
yListener);
|
||||
|
||||
private Window ownerWindow;
|
||||
|
||||
/**
|
||||
* Shows the pop over in a position relative to the edges of the given owner
|
||||
* node. The position is dependent on the arrow location. If the arrow is
|
||||
* pointing to the right then the pop over will be placed to the left of the
|
||||
* given owner. If the arrow points up then the pop over will be placed
|
||||
* below the given owner node. The arrow will slightly overlap with the
|
||||
* owner node.
|
||||
*
|
||||
* @param owner
|
||||
* the owner of the pop over
|
||||
*/
|
||||
public final void show(Node owner) {
|
||||
show(owner, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the pop over in a position relative to the edges of the given owner
|
||||
* node. The position is dependent on the arrow location. If the arrow is
|
||||
* pointing to the right then the pop over will be placed to the left of the
|
||||
* given owner. If the arrow points up then the pop over will be placed
|
||||
* below the given owner node.
|
||||
*
|
||||
* @param owner
|
||||
* the owner of the pop over
|
||||
* @param offset
|
||||
* if negative specifies the distance to the owner node or when
|
||||
* positive specifies the number of pixels that the arrow will
|
||||
* overlap with the owner node (positive values are recommended)
|
||||
*/
|
||||
public final void show(Node owner, double offset) {
|
||||
requireNonNull(owner);
|
||||
|
||||
Bounds bounds = owner.localToScreen(owner.getBoundsInLocal());
|
||||
|
||||
switch (getArrowLocation()) {
|
||||
case BOTTOM_CENTER:
|
||||
case BOTTOM_LEFT:
|
||||
case BOTTOM_RIGHT:
|
||||
show(owner, bounds.getMinX() + bounds.getWidth() / 2,
|
||||
bounds.getMinY() + offset);
|
||||
break;
|
||||
case LEFT_BOTTOM:
|
||||
case LEFT_CENTER:
|
||||
case LEFT_TOP:
|
||||
show(owner, bounds.getMaxX() - offset,
|
||||
bounds.getMinY() + bounds.getHeight() / 2);
|
||||
break;
|
||||
case RIGHT_BOTTOM:
|
||||
case RIGHT_CENTER:
|
||||
case RIGHT_TOP:
|
||||
show(owner, bounds.getMinX() + offset,
|
||||
bounds.getMinY() + bounds.getHeight() / 2);
|
||||
break;
|
||||
case TOP_CENTER:
|
||||
case TOP_LEFT:
|
||||
case TOP_RIGHT:
|
||||
show(owner, bounds.getMinX() + bounds.getWidth() / 2,
|
||||
bounds.getMinY() + bounds.getHeight() - offset);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the pop over visible at the give location and associates it with
|
||||
* the given owner node. The x and y coordinate will be the target location
|
||||
* of the arrow of the pop over and not the location of the window.
|
||||
*
|
||||
* @param owner
|
||||
* the owning node
|
||||
* @param x
|
||||
* the x coordinate for the pop over arrow tip
|
||||
* @param y
|
||||
* the y coordinate for the pop over arrow tip
|
||||
*/
|
||||
@Override
|
||||
public final void show(Node owner, double x, double y) {
|
||||
show(owner, x, y, DEFAULT_FADE_DURATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the pop over visible at the give location and associates it with
|
||||
* the given owner node. The x and y coordinate will be the target location
|
||||
* of the arrow of the pop over and not the location of the window.
|
||||
*
|
||||
* @param owner
|
||||
* the owning node
|
||||
* @param x
|
||||
* the x coordinate for the pop over arrow tip
|
||||
* @param y
|
||||
* the y coordinate for the pop over arrow tip
|
||||
* @param fadeInDuration
|
||||
* the time it takes for the pop over to be fully visible
|
||||
*/
|
||||
public final void show(Node owner, double x, double y,
|
||||
Duration fadeInDuration) {
|
||||
|
||||
/*
|
||||
* Calling show() a second time without first closing the
|
||||
* pop over causes it to be placed at the wrong location.
|
||||
*/
|
||||
if (ownerWindow != null && isShowing()) {
|
||||
super.hide();
|
||||
}
|
||||
|
||||
targetX = x;
|
||||
targetY = y;
|
||||
|
||||
if (owner == null) {
|
||||
throw new IllegalArgumentException("owner can not be null"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
if (fadeInDuration == null) {
|
||||
fadeInDuration = DEFAULT_FADE_DURATION;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is all needed because children windows do not get their x and y
|
||||
* coordinate updated when the owning window gets moved by the user.
|
||||
*/
|
||||
if (ownerWindow != null) {
|
||||
ownerWindow.xProperty().removeListener(weakXListener);
|
||||
ownerWindow.yProperty().removeListener(weakYListener);
|
||||
ownerWindow.widthProperty().removeListener(weakHideListener);
|
||||
ownerWindow.heightProperty().removeListener(weakHideListener);
|
||||
}
|
||||
|
||||
ownerWindow = owner.getScene().getWindow();
|
||||
ownerWindow.xProperty().addListener(weakXListener);
|
||||
ownerWindow.yProperty().addListener(weakYListener);
|
||||
ownerWindow.widthProperty().addListener(weakHideListener);
|
||||
ownerWindow.heightProperty().addListener(weakHideListener);
|
||||
|
||||
setOnShown(evt -> {
|
||||
|
||||
/*
|
||||
* The user clicked somewhere into the transparent background. If
|
||||
* this is the case the hide the window (when attached).
|
||||
*/
|
||||
getScene().addEventHandler(MOUSE_CLICKED,
|
||||
new EventHandler<MouseEvent>() {
|
||||
public void handle(MouseEvent evt) {
|
||||
if (evt.getTarget().equals(getScene().getRoot())) {
|
||||
if (!isDetached()) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/*
|
||||
* Move the window so that the arrow will end up pointing at the
|
||||
* target coordinates.
|
||||
*/
|
||||
adjustWindowLocation();
|
||||
});
|
||||
|
||||
super.show(owner, x, y);
|
||||
|
||||
// Fade In
|
||||
Node skinNode = getSkin().getNode();
|
||||
skinNode.setOpacity(0);
|
||||
|
||||
FadeTransition fadeIn = new FadeTransition(fadeInDuration, skinNode);
|
||||
fadeIn.setFromValue(0);
|
||||
fadeIn.setToValue(1);
|
||||
fadeIn.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the pop over by quickly changing its opacity to 0.
|
||||
*
|
||||
* @see #hide(Duration)
|
||||
*/
|
||||
@Override
|
||||
public final void hide() {
|
||||
hide(DEFAULT_FADE_DURATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the pop over by quickly changing its opacity to 0.
|
||||
*
|
||||
* @param fadeOutDuration
|
||||
* the duration of the fade transition that is being used to
|
||||
* change the opacity of the pop over
|
||||
* @since 1.0
|
||||
*/
|
||||
public final void hide(Duration fadeOutDuration) {
|
||||
if (fadeOutDuration == null) {
|
||||
fadeOutDuration = DEFAULT_FADE_DURATION;
|
||||
}
|
||||
|
||||
if (isShowing()) {
|
||||
// Fade Out
|
||||
Node skinNode = getSkin().getNode();
|
||||
skinNode.setOpacity(0);
|
||||
|
||||
FadeTransition fadeOut = new FadeTransition(fadeOutDuration,
|
||||
skinNode);
|
||||
fadeOut.setFromValue(1);
|
||||
fadeOut.setToValue(0);
|
||||
fadeOut.setOnFinished(evt -> super.hide());
|
||||
fadeOut.play();
|
||||
}
|
||||
}
|
||||
|
||||
private void adjustWindowLocation() {
|
||||
Bounds bounds = PopOver.this.getSkin().getNode().getBoundsInParent();
|
||||
|
||||
switch (getArrowLocation()) {
|
||||
case TOP_CENTER:
|
||||
case TOP_LEFT:
|
||||
case TOP_RIGHT:
|
||||
setX(getX() + bounds.getMinX() - computeXOffset());
|
||||
setY(getY() + bounds.getMinY() + getArrowSize());
|
||||
break;
|
||||
case LEFT_TOP:
|
||||
case LEFT_CENTER:
|
||||
case LEFT_BOTTOM:
|
||||
setX(getX() + bounds.getMinX() + getArrowSize());
|
||||
setY(getY() + bounds.getMinY() - computeYOffset());
|
||||
break;
|
||||
case BOTTOM_CENTER:
|
||||
case BOTTOM_LEFT:
|
||||
case BOTTOM_RIGHT:
|
||||
setX(getX() + bounds.getMinX() - computeXOffset());
|
||||
setY(getY() - bounds.getMinY() - bounds.getMaxY() - 1);
|
||||
break;
|
||||
case RIGHT_TOP:
|
||||
case RIGHT_BOTTOM:
|
||||
case RIGHT_CENTER:
|
||||
setX(getX() - bounds.getMinX() - bounds.getMaxX() - 1);
|
||||
setY(getY() + bounds.getMinY() - computeYOffset());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private double computeXOffset() {
|
||||
switch (getArrowLocation()) {
|
||||
case TOP_LEFT:
|
||||
case BOTTOM_LEFT:
|
||||
return getCornerRadius() + getArrowIndent() + getArrowSize();
|
||||
case TOP_CENTER:
|
||||
case BOTTOM_CENTER:
|
||||
return getContentNode().prefWidth(-1) / 2;
|
||||
case TOP_RIGHT:
|
||||
case BOTTOM_RIGHT:
|
||||
return getContentNode().prefWidth(-1) - getArrowIndent()
|
||||
- getCornerRadius() - getArrowSize();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private double computeYOffset() {
|
||||
double prefContentHeight = getContentNode().prefHeight(-1);
|
||||
|
||||
switch (getArrowLocation()) {
|
||||
case LEFT_TOP:
|
||||
case RIGHT_TOP:
|
||||
return getCornerRadius() + getArrowIndent() + getArrowSize();
|
||||
case LEFT_CENTER:
|
||||
case RIGHT_CENTER:
|
||||
return Math.max(prefContentHeight, 2 * (getCornerRadius()
|
||||
+ getArrowIndent() + getArrowSize())) / 2;
|
||||
case LEFT_BOTTOM:
|
||||
case RIGHT_BOTTOM:
|
||||
return Math.max(prefContentHeight - getCornerRadius()
|
||||
- getArrowIndent() - getArrowSize(), getCornerRadius()
|
||||
+ getArrowIndent() + getArrowSize());
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the pop over from the owning node. The pop over will no longer
|
||||
* display an arrow pointing at the owner node.
|
||||
*/
|
||||
public final void detach() {
|
||||
if (isDetachable()) {
|
||||
setDetached(true);
|
||||
}
|
||||
}
|
||||
|
||||
// detach support
|
||||
|
||||
private final BooleanProperty detachable = new SimpleBooleanProperty(this,
|
||||
"detachable", true); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Determines if the pop over is detachable at all.
|
||||
*/
|
||||
public final BooleanProperty detachableProperty() {
|
||||
return detachable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the detachable property.
|
||||
*
|
||||
* @param detachable
|
||||
* if true then the user can detach / tear off the pop over
|
||||
*
|
||||
* @see #detachableProperty()
|
||||
*/
|
||||
public final void setDetachable(boolean detachable) {
|
||||
detachableProperty().set(detachable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the detachable property.
|
||||
*
|
||||
* @return true if the user is allowed to detach / tear off the pop over
|
||||
*
|
||||
* @see #detachableProperty()
|
||||
*/
|
||||
public final boolean isDetachable() {
|
||||
return detachableProperty().get();
|
||||
}
|
||||
|
||||
private final BooleanProperty detached = new SimpleBooleanProperty(this,
|
||||
"detached", false); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Determines whether the pop over is detached from the owning node or not.
|
||||
* A detached pop over no longer shows an arrow pointing at the owner and
|
||||
* features its own title bar.
|
||||
*
|
||||
* @return the detached property
|
||||
*/
|
||||
public final BooleanProperty detachedProperty() {
|
||||
return detached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the detached property.
|
||||
*
|
||||
* @param detached
|
||||
* if true the pop over will change its apperance to "detached"
|
||||
* mode
|
||||
*
|
||||
* @see #detachedProperty()
|
||||
*/
|
||||
public final void setDetached(boolean detached) {
|
||||
detachedProperty().set(detached);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the detached property.
|
||||
*
|
||||
* @return true if the pop over is currently detached.
|
||||
*
|
||||
* @see #detachedProperty()
|
||||
*/
|
||||
public final boolean isDetached() {
|
||||
return detachedProperty().get();
|
||||
}
|
||||
|
||||
// arrow size support
|
||||
|
||||
// TODO: make styleable
|
||||
|
||||
private final DoubleProperty arrowSize = new SimpleDoubleProperty(this,
|
||||
"arrowSize", 12); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Controls the size of the arrow. Default value is 12.
|
||||
*
|
||||
* @return the arrow size property
|
||||
*/
|
||||
public final DoubleProperty arrowSizeProperty() {
|
||||
return arrowSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the arrow size property.
|
||||
*
|
||||
* @return the arrow size property value
|
||||
*
|
||||
* @see #arrowSizeProperty()
|
||||
*/
|
||||
public final double getArrowSize() {
|
||||
return arrowSizeProperty().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the arrow size property.
|
||||
*
|
||||
* @param size
|
||||
* the new value of the arrow size property
|
||||
*
|
||||
* @see #arrowSizeProperty()
|
||||
*/
|
||||
public final void setArrowSize(double size) {
|
||||
arrowSizeProperty().set(size);
|
||||
}
|
||||
|
||||
// arrow indent support
|
||||
|
||||
// TODO: make styleable
|
||||
|
||||
private final DoubleProperty arrowIndent = new SimpleDoubleProperty(this,
|
||||
"arrowIndent", 12); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Controls the distance between the arrow and the corners of the pop over.
|
||||
* The default value is 12.
|
||||
*
|
||||
* @return the arrow indent property
|
||||
*/
|
||||
public final DoubleProperty arrowIndentProperty() {
|
||||
return arrowIndent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the arrow indent property.
|
||||
*
|
||||
* @return the arrow indent value
|
||||
*
|
||||
* @see #arrowIndentProperty()
|
||||
*/
|
||||
public final double getArrowIndent() {
|
||||
return arrowIndentProperty().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the arrow indent property.
|
||||
*
|
||||
* @param size
|
||||
* the arrow indent value
|
||||
*
|
||||
* @see #arrowIndentProperty()
|
||||
*/
|
||||
public final void setArrowIndent(double size) {
|
||||
arrowIndentProperty().set(size);
|
||||
}
|
||||
|
||||
// radius support
|
||||
|
||||
// TODO: make styleable
|
||||
|
||||
private final DoubleProperty cornerRadius = new SimpleDoubleProperty(this,
|
||||
"cornerRadius", 6); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Returns the corner radius property for the pop over.
|
||||
*
|
||||
* @return the corner radius property (default is 6)
|
||||
*/
|
||||
public final DoubleProperty cornerRadiusProperty() {
|
||||
return cornerRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the corner radius property.
|
||||
*
|
||||
* @return the corner radius
|
||||
*
|
||||
* @see #cornerRadiusProperty()
|
||||
*/
|
||||
public final double getCornerRadius() {
|
||||
return cornerRadiusProperty().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the corner radius property.
|
||||
*
|
||||
* @param radius
|
||||
* the corner radius
|
||||
*
|
||||
* @see #cornerRadiusProperty()
|
||||
*/
|
||||
public final void setCornerRadius(double radius) {
|
||||
cornerRadiusProperty().set(radius);
|
||||
}
|
||||
|
||||
// Detached stage title
|
||||
|
||||
private final StringProperty detachedTitle = new SimpleStringProperty(this,
|
||||
"detachedTitle", "Info"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/**
|
||||
* Stores the title to display when the pop over becomes detached.
|
||||
*
|
||||
* @return the detached title property
|
||||
*/
|
||||
public final StringProperty detachedTitleProperty() {
|
||||
return detachedTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the detached title property.
|
||||
*
|
||||
* @return the detached title
|
||||
*
|
||||
* @see #detachedTitleProperty()
|
||||
*/
|
||||
public final String getDetachedTitle() {
|
||||
return detachedTitleProperty().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the detached title property.
|
||||
*
|
||||
* @param title
|
||||
* the title to use when detached
|
||||
*
|
||||
* @see #detachedTitleProperty()
|
||||
*/
|
||||
public final void setDetachedTitle(String title) {
|
||||
if (title == null) {
|
||||
throw new IllegalArgumentException("title can not be null"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
detachedTitleProperty().set(title);
|
||||
}
|
||||
|
||||
private final ObjectProperty<ArrowLocation> arrowLocation = new SimpleObjectProperty<PopOver.ArrowLocation>(
|
||||
this, "arrowLocation", ArrowLocation.LEFT_TOP); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Stores the preferred arrow location. This might not be the actual
|
||||
* location of the arrow if auto fix is enabled.
|
||||
*
|
||||
* @see #setAutoFix(boolean)
|
||||
*
|
||||
* @return the arrow location property
|
||||
*/
|
||||
public final ObjectProperty<ArrowLocation> arrowLocationProperty() {
|
||||
return arrowLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the arrow location property.
|
||||
*
|
||||
* @see #arrowLocationProperty()
|
||||
*
|
||||
* @param location
|
||||
* the requested location
|
||||
*/
|
||||
public final void setArrowLocation(ArrowLocation location) {
|
||||
arrowLocationProperty().set(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the arrow location property.
|
||||
*
|
||||
* @see #arrowLocationProperty()
|
||||
*
|
||||
* @return the preferred arrow location
|
||||
*/
|
||||
public final ArrowLocation getArrowLocation() {
|
||||
return arrowLocationProperty().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* All possible arrow locations.
|
||||
*/
|
||||
public enum ArrowLocation {
|
||||
LEFT_TOP, LEFT_CENTER, LEFT_BOTTOM, RIGHT_TOP, RIGHT_CENTER, RIGHT_BOTTOM, TOP_LEFT, TOP_CENTER, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
.popover {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.popover > .border {
|
||||
-fx-stroke: linear-gradient(to bottom, rgba(0,0,0, .3), rgba(0, 0, 0, .7)) ;
|
||||
-fx-stroke-width: 0.5;
|
||||
-fx-fill: rgba(255.0,255.0,255.0, .95);
|
||||
-fx-effect: dropshadow(gaussian, rgba(0,0,0,.2), 10.0, 0.5, 2.0, 2.0);
|
||||
}
|
||||
|
||||
.popover > .content {
|
||||
}
|
||||
|
||||
.popover > .detached {
|
||||
}
|
||||
|
||||
.popover > .content > .title > .text {
|
||||
-fx-padding: 6.0 6.0 0.0 6.0;
|
||||
-fx-text-fill: rgba(120, 120, 120, .8);
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
.popover > .content > .title > .icon {
|
||||
-fx-padding: 6.0 0.0 0.0 10.0;
|
||||
}
|
||||
|
||||
.popover > .content > .title > .icon > .graphics > .circle {
|
||||
-fx-fill: gray ;
|
||||
-fx-effect: innershadow(gaussian, rgba(0,0,0,.2), 3, 0.5, 1.0, 1.0);
|
||||
}
|
||||
|
||||
.popover > .content > .title > .icon > .graphics > .line {
|
||||
-fx-stroke: white ;
|
||||
-fx-stroke-width: 2;
|
||||
}
|
@ -0,0 +1,693 @@
|
||||
/**
|
||||
* Copyright (c) 2013 - 2015, ControlsFX
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * 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.
|
||||
* * Neither the name of ControlsFX, any associated website, nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 CONTROLSFX 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.
|
||||
*/
|
||||
package bisq.desktop.components.controlsfx.skin;
|
||||
|
||||
import static java.lang.Double.MAX_VALUE;
|
||||
import static javafx.geometry.Pos.CENTER_LEFT;
|
||||
import static javafx.scene.control.ContentDisplay.GRAPHIC_ONLY;
|
||||
import static bisq.desktop.components.controlsfx.control.PopOver.ArrowLocation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.property.SimpleDoubleProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.shape.Circle;
|
||||
import javafx.scene.shape.HLineTo;
|
||||
import javafx.scene.shape.Line;
|
||||
import javafx.scene.shape.LineTo;
|
||||
import javafx.scene.shape.MoveTo;
|
||||
import javafx.scene.shape.Path;
|
||||
import javafx.scene.shape.PathElement;
|
||||
import javafx.scene.shape.QuadCurveTo;
|
||||
import javafx.scene.shape.VLineTo;
|
||||
import javafx.stage.Window;
|
||||
|
||||
import bisq.desktop.components.controlsfx.control.PopOver;
|
||||
import bisq.desktop.components.controlsfx.control.PopOver.ArrowLocation;
|
||||
|
||||
public class PopOverSkin implements Skin<PopOver> {
|
||||
|
||||
private static final String DETACHED_STYLE_CLASS = "detached"; //$NON-NLS-1$
|
||||
|
||||
private double xOffset;
|
||||
private double yOffset;
|
||||
|
||||
private boolean tornOff;
|
||||
|
||||
private Label title;
|
||||
private Label closeIcon;
|
||||
|
||||
private Path path;
|
||||
private BorderPane content;
|
||||
private StackPane titlePane;
|
||||
private StackPane stackPane;
|
||||
|
||||
private Point2D dragStartLocation;
|
||||
|
||||
private PopOver popOver;
|
||||
|
||||
public PopOverSkin(final PopOver popOver) {
|
||||
|
||||
this.popOver = popOver;
|
||||
|
||||
stackPane = new StackPane();
|
||||
stackPane.getStylesheets().add(
|
||||
PopOver.class.getResource("popover.css").toExternalForm()); //$NON-NLS-1$
|
||||
stackPane.setPickOnBounds(false);
|
||||
stackPane.getStyleClass().add("popover"); //$NON-NLS-1$
|
||||
|
||||
/*
|
||||
* The min width and height equal 2 * corner radius + 2 * arrow indent +
|
||||
* 2 * arrow size.
|
||||
*/
|
||||
stackPane.minWidthProperty().bind(
|
||||
Bindings.add(Bindings.multiply(2, popOver.arrowSizeProperty()),
|
||||
Bindings.add(
|
||||
Bindings.multiply(2,
|
||||
popOver.cornerRadiusProperty()),
|
||||
Bindings.multiply(2,
|
||||
popOver.arrowIndentProperty()))));
|
||||
|
||||
stackPane.minHeightProperty().bind(stackPane.minWidthProperty());
|
||||
|
||||
title = new Label();
|
||||
title.textProperty().bind(popOver.detachedTitleProperty());
|
||||
title.setMaxSize(MAX_VALUE, MAX_VALUE);
|
||||
title.setAlignment(Pos.CENTER);
|
||||
title.getStyleClass().add("text"); //$NON-NLS-1$
|
||||
|
||||
closeIcon = new Label();
|
||||
closeIcon.setGraphic(createCloseIcon());
|
||||
closeIcon.setMaxSize(MAX_VALUE, MAX_VALUE);
|
||||
closeIcon.setContentDisplay(GRAPHIC_ONLY);
|
||||
closeIcon.visibleProperty().bind(popOver.detachedProperty());
|
||||
closeIcon.getStyleClass().add("icon"); //$NON-NLS-1$
|
||||
closeIcon.setAlignment(CENTER_LEFT);
|
||||
closeIcon.getGraphic().setOnMouseClicked(
|
||||
new EventHandler<MouseEvent>() {
|
||||
@Override
|
||||
public void handle(MouseEvent evt) {
|
||||
popOver.hide();
|
||||
}
|
||||
});
|
||||
|
||||
titlePane = new StackPane();
|
||||
titlePane.getChildren().add(title);
|
||||
titlePane.getChildren().add(closeIcon);
|
||||
titlePane.getStyleClass().add("title"); //$NON-NLS-1$
|
||||
|
||||
content = new BorderPane();
|
||||
content.setCenter(popOver.getContentNode());
|
||||
content.getStyleClass().add("content"); //$NON-NLS-1$
|
||||
|
||||
if (popOver.isDetached()) {
|
||||
content.setTop(titlePane);
|
||||
popOver.getStyleClass().add(DETACHED_STYLE_CLASS);
|
||||
content.getStyleClass().add(DETACHED_STYLE_CLASS);
|
||||
}
|
||||
|
||||
InvalidationListener updatePathListener = new InvalidationListener() {
|
||||
|
||||
@Override
|
||||
public void invalidated(Observable observable) {
|
||||
updatePath();
|
||||
}
|
||||
};
|
||||
|
||||
getPopupWindow().xProperty().addListener(updatePathListener);
|
||||
getPopupWindow().yProperty().addListener(updatePathListener);
|
||||
|
||||
popOver.arrowLocationProperty().addListener(updatePathListener);
|
||||
|
||||
popOver.contentNodeProperty().addListener(new ChangeListener<Node>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Node> value,
|
||||
Node oldContent, Node newContent) {
|
||||
content.setCenter(newContent);
|
||||
}
|
||||
});
|
||||
|
||||
popOver.detachedProperty().addListener(new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> value,
|
||||
Boolean oldDetached, Boolean newDetached) {
|
||||
|
||||
updatePath();
|
||||
|
||||
if (newDetached) {
|
||||
popOver.getStyleClass().add(DETACHED_STYLE_CLASS);
|
||||
content.getStyleClass().add(DETACHED_STYLE_CLASS);
|
||||
content.setTop(titlePane);
|
||||
} else {
|
||||
popOver.getStyleClass().remove(DETACHED_STYLE_CLASS);
|
||||
content.getStyleClass().remove(DETACHED_STYLE_CLASS);
|
||||
content.setTop(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
path = new Path();
|
||||
path.getStyleClass().add("border"); //$NON-NLS-1$
|
||||
path.setManaged(false);
|
||||
|
||||
createPathElements();
|
||||
updatePath();
|
||||
|
||||
final EventHandler<MouseEvent> mousePressedHandler = new EventHandler<MouseEvent>() {
|
||||
public void handle(MouseEvent evt) {
|
||||
if (popOver.isDetachable() || popOver.isDetached()) {
|
||||
tornOff = false;
|
||||
|
||||
xOffset = evt.getScreenX();
|
||||
yOffset = evt.getScreenY();
|
||||
|
||||
dragStartLocation = new Point2D(xOffset, yOffset);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
final EventHandler<MouseEvent> mouseReleasedHandler = new EventHandler<MouseEvent>() {
|
||||
public void handle(MouseEvent evt) {
|
||||
if (tornOff && !getSkinnable().isDetached()) {
|
||||
tornOff = false;
|
||||
getSkinnable().detach();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
final EventHandler<MouseEvent> mouseDragHandler = new EventHandler<MouseEvent>() {
|
||||
|
||||
public void handle(MouseEvent evt) {
|
||||
if (popOver.isDetachable() || popOver.isDetached()) {
|
||||
double deltaX = evt.getScreenX() - xOffset;
|
||||
double deltaY = evt.getScreenY() - yOffset;
|
||||
|
||||
Window window = getSkinnable().getScene().getWindow();
|
||||
|
||||
window.setX(window.getX() + deltaX);
|
||||
window.setY(window.getY() + deltaY);
|
||||
|
||||
xOffset = evt.getScreenX();
|
||||
yOffset = evt.getScreenY();
|
||||
|
||||
if (dragStartLocation.distance(xOffset, yOffset) > 20) {
|
||||
tornOff = true;
|
||||
updatePath();
|
||||
} else if (tornOff) {
|
||||
tornOff = false;
|
||||
updatePath();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
stackPane.setOnMousePressed(mousePressedHandler);
|
||||
stackPane.setOnMouseDragged(mouseDragHandler);
|
||||
stackPane.setOnMouseReleased(mouseReleasedHandler);
|
||||
|
||||
stackPane.getChildren().add(path);
|
||||
stackPane.getChildren().add(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode() {
|
||||
return stackPane;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PopOver getSkinnable() {
|
||||
return popOver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
private Node createCloseIcon() {
|
||||
Group group = new Group();
|
||||
group.getStyleClass().add("graphics"); //$NON-NLS-1$
|
||||
|
||||
Circle circle = new Circle();
|
||||
circle.getStyleClass().add("circle"); //$NON-NLS-1$
|
||||
circle.setRadius(6);
|
||||
circle.setCenterX(6);
|
||||
circle.setCenterY(6);
|
||||
group.getChildren().add(circle);
|
||||
|
||||
Line line1 = new Line();
|
||||
line1.getStyleClass().add("line"); //$NON-NLS-1$
|
||||
line1.setStartX(4);
|
||||
line1.setStartY(4);
|
||||
line1.setEndX(8);
|
||||
line1.setEndY(8);
|
||||
group.getChildren().add(line1);
|
||||
|
||||
Line line2 = new Line();
|
||||
line2.getStyleClass().add("line"); //$NON-NLS-1$
|
||||
line2.setStartX(8);
|
||||
line2.setStartY(4);
|
||||
line2.setEndX(4);
|
||||
line2.setEndY(8);
|
||||
group.getChildren().add(line2);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
private MoveTo moveTo;
|
||||
|
||||
private QuadCurveTo topCurveTo, rightCurveTo, bottomCurveTo, leftCurveTo;
|
||||
|
||||
private HLineTo lineBTop, lineETop, lineHTop, lineKTop;
|
||||
private LineTo lineCTop, lineDTop, lineFTop, lineGTop, lineITop, lineJTop;
|
||||
|
||||
private VLineTo lineBRight, lineERight, lineHRight, lineKRight;
|
||||
private LineTo lineCRight, lineDRight, lineFRight, lineGRight, lineIRight,
|
||||
lineJRight;
|
||||
|
||||
private HLineTo lineBBottom, lineEBottom, lineHBottom, lineKBottom;
|
||||
private LineTo lineCBottom, lineDBottom, lineFBottom, lineGBottom,
|
||||
lineIBottom, lineJBottom;
|
||||
|
||||
private VLineTo lineBLeft, lineELeft, lineHLeft, lineKLeft;
|
||||
private LineTo lineCLeft, lineDLeft, lineFLeft, lineGLeft, lineILeft,
|
||||
lineJLeft;
|
||||
|
||||
private void createPathElements() {
|
||||
DoubleProperty centerYProperty = new SimpleDoubleProperty();
|
||||
DoubleProperty centerXProperty = new SimpleDoubleProperty();
|
||||
|
||||
DoubleProperty leftEdgeProperty = new SimpleDoubleProperty();
|
||||
DoubleProperty leftEdgePlusRadiusProperty = new SimpleDoubleProperty();
|
||||
|
||||
DoubleProperty topEdgeProperty = new SimpleDoubleProperty();
|
||||
DoubleProperty topEdgePlusRadiusProperty = new SimpleDoubleProperty();
|
||||
|
||||
DoubleProperty rightEdgeProperty = new SimpleDoubleProperty();
|
||||
DoubleProperty rightEdgeMinusRadiusProperty = new SimpleDoubleProperty();
|
||||
|
||||
DoubleProperty bottomEdgeProperty = new SimpleDoubleProperty();
|
||||
DoubleProperty bottomEdgeMinusRadiusProperty = new SimpleDoubleProperty();
|
||||
|
||||
DoubleProperty cornerProperty = getSkinnable().cornerRadiusProperty();
|
||||
|
||||
DoubleProperty arrowSizeProperty = getSkinnable().arrowSizeProperty();
|
||||
DoubleProperty arrowIndentProperty = getSkinnable()
|
||||
.arrowIndentProperty();
|
||||
|
||||
centerYProperty.bind(Bindings.divide(stackPane.heightProperty(), 2));
|
||||
centerXProperty.bind(Bindings.divide(stackPane.widthProperty(), 2));
|
||||
|
||||
leftEdgePlusRadiusProperty.bind(Bindings.add(leftEdgeProperty,
|
||||
getSkinnable().cornerRadiusProperty()));
|
||||
|
||||
topEdgePlusRadiusProperty.bind(Bindings.add(topEdgeProperty,
|
||||
getSkinnable().cornerRadiusProperty()));
|
||||
|
||||
rightEdgeProperty.bind(stackPane.widthProperty());
|
||||
rightEdgeMinusRadiusProperty.bind(Bindings.subtract(rightEdgeProperty,
|
||||
getSkinnable().cornerRadiusProperty()));
|
||||
|
||||
bottomEdgeProperty.bind(stackPane.heightProperty());
|
||||
bottomEdgeMinusRadiusProperty.bind(Bindings.subtract(
|
||||
bottomEdgeProperty, getSkinnable().cornerRadiusProperty()));
|
||||
|
||||
// INIT
|
||||
moveTo = new MoveTo();
|
||||
moveTo.xProperty().bind(leftEdgePlusRadiusProperty);
|
||||
moveTo.yProperty().bind(topEdgeProperty);
|
||||
|
||||
//
|
||||
// TOP EDGE
|
||||
//
|
||||
lineBTop = new HLineTo();
|
||||
lineBTop.xProperty().bind(
|
||||
Bindings.add(leftEdgePlusRadiusProperty, arrowIndentProperty));
|
||||
|
||||
lineCTop = new LineTo();
|
||||
lineCTop.xProperty().bind(
|
||||
Bindings.add(lineBTop.xProperty(), arrowSizeProperty));
|
||||
lineCTop.yProperty().bind(
|
||||
Bindings.subtract(topEdgeProperty, arrowSizeProperty));
|
||||
|
||||
lineDTop = new LineTo();
|
||||
lineDTop.xProperty().bind(
|
||||
Bindings.add(lineCTop.xProperty(), arrowSizeProperty));
|
||||
lineDTop.yProperty().bind(topEdgeProperty);
|
||||
|
||||
lineETop = new HLineTo();
|
||||
lineETop.xProperty().bind(
|
||||
Bindings.subtract(centerXProperty, arrowSizeProperty));
|
||||
|
||||
lineFTop = new LineTo();
|
||||
lineFTop.xProperty().bind(centerXProperty);
|
||||
lineFTop.yProperty().bind(
|
||||
Bindings.subtract(topEdgeProperty, arrowSizeProperty));
|
||||
|
||||
lineGTop = new LineTo();
|
||||
lineGTop.xProperty().bind(
|
||||
Bindings.add(centerXProperty, arrowSizeProperty));
|
||||
lineGTop.yProperty().bind(topEdgeProperty);
|
||||
|
||||
lineHTop = new HLineTo();
|
||||
lineHTop.xProperty().bind(
|
||||
Bindings.subtract(Bindings.subtract(
|
||||
rightEdgeMinusRadiusProperty, arrowIndentProperty),
|
||||
Bindings.multiply(arrowSizeProperty, 2)));
|
||||
|
||||
lineITop = new LineTo();
|
||||
lineITop.xProperty().bind(
|
||||
Bindings.subtract(Bindings.subtract(
|
||||
rightEdgeMinusRadiusProperty, arrowIndentProperty),
|
||||
arrowSizeProperty));
|
||||
lineITop.yProperty().bind(
|
||||
Bindings.subtract(topEdgeProperty, arrowSizeProperty));
|
||||
|
||||
lineJTop = new LineTo();
|
||||
lineJTop.xProperty().bind(
|
||||
Bindings.subtract(rightEdgeMinusRadiusProperty,
|
||||
arrowIndentProperty));
|
||||
lineJTop.yProperty().bind(topEdgeProperty);
|
||||
|
||||
lineKTop = new HLineTo();
|
||||
lineKTop.xProperty().bind(rightEdgeMinusRadiusProperty);
|
||||
|
||||
//
|
||||
// RIGHT EDGE
|
||||
//
|
||||
rightCurveTo = new QuadCurveTo();
|
||||
rightCurveTo.xProperty().bind(rightEdgeProperty);
|
||||
rightCurveTo.yProperty().bind(
|
||||
Bindings.add(topEdgeProperty, cornerProperty));
|
||||
rightCurveTo.controlXProperty().bind(rightEdgeProperty);
|
||||
rightCurveTo.controlYProperty().bind(topEdgeProperty);
|
||||
|
||||
lineBRight = new VLineTo();
|
||||
lineBRight.yProperty().bind(
|
||||
Bindings.add(topEdgePlusRadiusProperty, arrowIndentProperty));
|
||||
|
||||
lineCRight = new LineTo();
|
||||
lineCRight.xProperty().bind(
|
||||
Bindings.add(rightEdgeProperty, arrowSizeProperty));
|
||||
lineCRight.yProperty().bind(
|
||||
Bindings.add(lineBRight.yProperty(), arrowSizeProperty));
|
||||
|
||||
lineDRight = new LineTo();
|
||||
lineDRight.xProperty().bind(rightEdgeProperty);
|
||||
lineDRight.yProperty().bind(
|
||||
Bindings.add(lineCRight.yProperty(), arrowSizeProperty));
|
||||
|
||||
lineERight = new VLineTo();
|
||||
lineERight.yProperty().bind(
|
||||
Bindings.subtract(centerYProperty, arrowSizeProperty));
|
||||
|
||||
lineFRight = new LineTo();
|
||||
lineFRight.xProperty().bind(
|
||||
Bindings.add(rightEdgeProperty, arrowSizeProperty));
|
||||
lineFRight.yProperty().bind(centerYProperty);
|
||||
|
||||
lineGRight = new LineTo();
|
||||
lineGRight.xProperty().bind(rightEdgeProperty);
|
||||
lineGRight.yProperty().bind(
|
||||
Bindings.add(centerYProperty, arrowSizeProperty));
|
||||
|
||||
lineHRight = new VLineTo();
|
||||
lineHRight.yProperty().bind(
|
||||
Bindings.subtract(Bindings.subtract(
|
||||
bottomEdgeMinusRadiusProperty, arrowIndentProperty),
|
||||
Bindings.multiply(arrowSizeProperty, 2)));
|
||||
|
||||
lineIRight = new LineTo();
|
||||
lineIRight.xProperty().bind(
|
||||
Bindings.add(rightEdgeProperty, arrowSizeProperty));
|
||||
lineIRight.yProperty().bind(
|
||||
Bindings.subtract(Bindings.subtract(
|
||||
bottomEdgeMinusRadiusProperty, arrowIndentProperty),
|
||||
arrowSizeProperty));
|
||||
|
||||
lineJRight = new LineTo();
|
||||
lineJRight.xProperty().bind(rightEdgeProperty);
|
||||
lineJRight.yProperty().bind(
|
||||
Bindings.subtract(bottomEdgeMinusRadiusProperty,
|
||||
arrowIndentProperty));
|
||||
|
||||
lineKRight = new VLineTo();
|
||||
lineKRight.yProperty().bind(bottomEdgeMinusRadiusProperty);
|
||||
|
||||
//
|
||||
// BOTTOM EDGE
|
||||
//
|
||||
|
||||
bottomCurveTo = new QuadCurveTo();
|
||||
bottomCurveTo.xProperty().bind(rightEdgeMinusRadiusProperty);
|
||||
bottomCurveTo.yProperty().bind(bottomEdgeProperty);
|
||||
bottomCurveTo.controlXProperty().bind(rightEdgeProperty);
|
||||
bottomCurveTo.controlYProperty().bind(bottomEdgeProperty);
|
||||
|
||||
lineBBottom = new HLineTo();
|
||||
lineBBottom.xProperty().bind(
|
||||
Bindings.subtract(rightEdgeMinusRadiusProperty,
|
||||
arrowIndentProperty));
|
||||
|
||||
lineCBottom = new LineTo();
|
||||
lineCBottom.xProperty().bind(
|
||||
Bindings.subtract(lineBBottom.xProperty(), arrowSizeProperty));
|
||||
lineCBottom.yProperty().bind(
|
||||
Bindings.add(bottomEdgeProperty, arrowSizeProperty));
|
||||
|
||||
lineDBottom = new LineTo();
|
||||
lineDBottom.xProperty().bind(
|
||||
Bindings.subtract(lineCBottom.xProperty(), arrowSizeProperty));
|
||||
lineDBottom.yProperty().bind(bottomEdgeProperty);
|
||||
|
||||
lineEBottom = new HLineTo();
|
||||
lineEBottom.xProperty().bind(
|
||||
Bindings.add(centerXProperty, arrowSizeProperty));
|
||||
|
||||
lineFBottom = new LineTo();
|
||||
lineFBottom.xProperty().bind(centerXProperty);
|
||||
lineFBottom.yProperty().bind(
|
||||
Bindings.add(bottomEdgeProperty, arrowSizeProperty));
|
||||
|
||||
lineGBottom = new LineTo();
|
||||
lineGBottom.xProperty().bind(
|
||||
Bindings.subtract(centerXProperty, arrowSizeProperty));
|
||||
lineGBottom.yProperty().bind(bottomEdgeProperty);
|
||||
|
||||
lineHBottom = new HLineTo();
|
||||
lineHBottom.xProperty().bind(
|
||||
Bindings.add(Bindings.add(leftEdgePlusRadiusProperty,
|
||||
arrowIndentProperty), Bindings.multiply(
|
||||
arrowSizeProperty, 2)));
|
||||
|
||||
lineIBottom = new LineTo();
|
||||
lineIBottom.xProperty().bind(
|
||||
Bindings.add(Bindings.add(leftEdgePlusRadiusProperty,
|
||||
arrowIndentProperty), arrowSizeProperty));
|
||||
lineIBottom.yProperty().bind(
|
||||
Bindings.add(bottomEdgeProperty, arrowSizeProperty));
|
||||
|
||||
lineJBottom = new LineTo();
|
||||
lineJBottom.xProperty().bind(
|
||||
Bindings.add(leftEdgePlusRadiusProperty, arrowIndentProperty));
|
||||
lineJBottom.yProperty().bind(bottomEdgeProperty);
|
||||
|
||||
lineKBottom = new HLineTo();
|
||||
lineKBottom.xProperty().bind(leftEdgePlusRadiusProperty);
|
||||
|
||||
//
|
||||
// LEFT EDGE
|
||||
//
|
||||
leftCurveTo = new QuadCurveTo();
|
||||
leftCurveTo.xProperty().bind(leftEdgeProperty);
|
||||
leftCurveTo.yProperty().bind(
|
||||
Bindings.subtract(bottomEdgeProperty, cornerProperty));
|
||||
leftCurveTo.controlXProperty().bind(leftEdgeProperty);
|
||||
leftCurveTo.controlYProperty().bind(bottomEdgeProperty);
|
||||
|
||||
lineBLeft = new VLineTo();
|
||||
lineBLeft.yProperty().bind(
|
||||
Bindings.subtract(bottomEdgeMinusRadiusProperty,
|
||||
arrowIndentProperty));
|
||||
|
||||
lineCLeft = new LineTo();
|
||||
lineCLeft.xProperty().bind(
|
||||
Bindings.subtract(leftEdgeProperty, arrowSizeProperty));
|
||||
lineCLeft.yProperty().bind(
|
||||
Bindings.subtract(lineBLeft.yProperty(), arrowSizeProperty));
|
||||
|
||||
lineDLeft = new LineTo();
|
||||
lineDLeft.xProperty().bind(leftEdgeProperty);
|
||||
lineDLeft.yProperty().bind(
|
||||
Bindings.subtract(lineCLeft.yProperty(), arrowSizeProperty));
|
||||
|
||||
lineELeft = new VLineTo();
|
||||
lineELeft.yProperty().bind(
|
||||
Bindings.add(centerYProperty, arrowSizeProperty));
|
||||
|
||||
lineFLeft = new LineTo();
|
||||
lineFLeft.xProperty().bind(
|
||||
Bindings.subtract(leftEdgeProperty, arrowSizeProperty));
|
||||
lineFLeft.yProperty().bind(centerYProperty);
|
||||
|
||||
lineGLeft = new LineTo();
|
||||
lineGLeft.xProperty().bind(leftEdgeProperty);
|
||||
lineGLeft.yProperty().bind(
|
||||
Bindings.subtract(centerYProperty, arrowSizeProperty));
|
||||
|
||||
lineHLeft = new VLineTo();
|
||||
lineHLeft.yProperty().bind(
|
||||
Bindings.add(Bindings.add(topEdgePlusRadiusProperty,
|
||||
arrowIndentProperty), Bindings.multiply(
|
||||
arrowSizeProperty, 2)));
|
||||
|
||||
lineILeft = new LineTo();
|
||||
lineILeft.xProperty().bind(
|
||||
Bindings.subtract(leftEdgeProperty, arrowSizeProperty));
|
||||
lineILeft.yProperty().bind(
|
||||
Bindings.add(Bindings.add(topEdgePlusRadiusProperty,
|
||||
arrowIndentProperty), arrowSizeProperty));
|
||||
|
||||
lineJLeft = new LineTo();
|
||||
lineJLeft.xProperty().bind(leftEdgeProperty);
|
||||
lineJLeft.yProperty().bind(
|
||||
Bindings.add(topEdgePlusRadiusProperty, arrowIndentProperty));
|
||||
|
||||
lineKLeft = new VLineTo();
|
||||
lineKLeft.yProperty().bind(topEdgePlusRadiusProperty);
|
||||
|
||||
topCurveTo = new QuadCurveTo();
|
||||
topCurveTo.xProperty().bind(leftEdgePlusRadiusProperty);
|
||||
topCurveTo.yProperty().bind(topEdgeProperty);
|
||||
topCurveTo.controlXProperty().bind(leftEdgeProperty);
|
||||
topCurveTo.controlYProperty().bind(topEdgeProperty);
|
||||
}
|
||||
|
||||
private Window getPopupWindow() {
|
||||
return getSkinnable().getScene().getWindow();
|
||||
}
|
||||
|
||||
private boolean showArrow(ArrowLocation location) {
|
||||
ArrowLocation arrowLocation = getSkinnable().getArrowLocation();
|
||||
return location.equals(arrowLocation) && !getSkinnable().isDetached()
|
||||
&& !tornOff;
|
||||
}
|
||||
|
||||
private void updatePath() {
|
||||
List<PathElement> elements = new ArrayList<>();
|
||||
elements.add(moveTo);
|
||||
|
||||
if (showArrow(TOP_LEFT)) {
|
||||
elements.add(lineBTop);
|
||||
elements.add(lineCTop);
|
||||
elements.add(lineDTop);
|
||||
}
|
||||
if (showArrow(TOP_CENTER)) {
|
||||
elements.add(lineETop);
|
||||
elements.add(lineFTop);
|
||||
elements.add(lineGTop);
|
||||
}
|
||||
if (showArrow(TOP_RIGHT)) {
|
||||
elements.add(lineHTop);
|
||||
elements.add(lineITop);
|
||||
elements.add(lineJTop);
|
||||
}
|
||||
elements.add(lineKTop);
|
||||
elements.add(rightCurveTo);
|
||||
|
||||
if (showArrow(RIGHT_TOP)) {
|
||||
elements.add(lineBRight);
|
||||
elements.add(lineCRight);
|
||||
elements.add(lineDRight);
|
||||
}
|
||||
if (showArrow(RIGHT_CENTER)) {
|
||||
elements.add(lineERight);
|
||||
elements.add(lineFRight);
|
||||
elements.add(lineGRight);
|
||||
}
|
||||
if (showArrow(RIGHT_BOTTOM)) {
|
||||
elements.add(lineHRight);
|
||||
elements.add(lineIRight);
|
||||
elements.add(lineJRight);
|
||||
}
|
||||
elements.add(lineKRight);
|
||||
elements.add(bottomCurveTo);
|
||||
|
||||
if (showArrow(BOTTOM_RIGHT)) {
|
||||
elements.add(lineBBottom);
|
||||
elements.add(lineCBottom);
|
||||
elements.add(lineDBottom);
|
||||
}
|
||||
if (showArrow(BOTTOM_CENTER)) {
|
||||
elements.add(lineEBottom);
|
||||
elements.add(lineFBottom);
|
||||
elements.add(lineGBottom);
|
||||
}
|
||||
if (showArrow(BOTTOM_LEFT)) {
|
||||
elements.add(lineHBottom);
|
||||
elements.add(lineIBottom);
|
||||
elements.add(lineJBottom);
|
||||
}
|
||||
elements.add(lineKBottom);
|
||||
elements.add(leftCurveTo);
|
||||
|
||||
if (showArrow(LEFT_BOTTOM)) {
|
||||
elements.add(lineBLeft);
|
||||
elements.add(lineCLeft);
|
||||
elements.add(lineDLeft);
|
||||
}
|
||||
if (showArrow(LEFT_CENTER)) {
|
||||
elements.add(lineELeft);
|
||||
elements.add(lineFLeft);
|
||||
elements.add(lineGLeft);
|
||||
}
|
||||
if (showArrow(LEFT_TOP)) {
|
||||
elements.add(lineHLeft);
|
||||
elements.add(lineILeft);
|
||||
elements.add(lineJLeft);
|
||||
}
|
||||
elements.add(lineKLeft);
|
||||
elements.add(topCurveTo);
|
||||
|
||||
path.getElements().setAll(elements);
|
||||
}
|
||||
}
|
@ -14,7 +14,6 @@
|
||||
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'org.controlsfx:controlsfx:b98f1c9507c05600f80323674b33d15674926c71b0116f70085b62bdacf1e573',
|
||||
'net.glxn:qrgen:c85d9d8512d91e8ad11fe56259a7825bd50ce0245447e236cf168d1b17591882',
|
||||
'de.jensd:fontawesomefx:73bacc991a0a6f5cf0f911767c8db161e0949dbca61e8371eb4342e3da96887b',
|
||||
'de.jensd:fontawesomefx-materialdesignfont:dbad8dfdd1c85e298d5bbae25b2399aec9e85064db57b2427d10f3815aa98752',
|
||||
|
Loading…
Reference in New Issue
Block a user