package me.stieglmaier.sphereMiners.view;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
/**
* A StackPane that <i>scales</i> its contents to fit (preserving aspect ratio),
* or fill (scaling independently in X and Y) the available area.
* <p>
* Note <code>AutoScalingStackPane</code> applies to the contents a scaling
* transformation rather than attempting to resize the contents.
* <p>
* If the contents is a Canvas with pixel dimension 50 by 50, after scaling the
* Canvas still will have 50 by 50 pixels and the appearance may be pixelated
* (this might be desired if the application is interfacing a camera and the
* Canvas needs to match in size the camera's CCD size).
* <p>
* If the content contains FX Controls then these get magnified rather than
* resized, that is all text and graphics are scaled (this might be desired for
* Point of Sale full screen applications)
* <h3>Known Limitations</h3>
* Rescaling occurs only when the AutoScalingStackPane is resized, it does not
* occur automatically if and when the content changes size.
*
*
* @author michaelellis
*/
public class AutoScalingStackPane extends StackPane {
/**
* Force scale transformation to be recomputed based on the size of this
* <code>AutoScalingStackPane</code> and the size of the contents.
*/
public void rescale() {
if (!getChildren().isEmpty()) {
getChildren()
.forEach(
(c) -> {
double xScale = getWidth() / c.getBoundsInLocal().getWidth();
double yScale = getHeight() / c.getBoundsInLocal().getHeight();
if (autoScale.get() == AutoScale.FILL) {
c.setScaleX(xScale);
c.setScaleY(yScale);
} else if (autoScale.get() == AutoScale.FIT) {
double scale = Math.min(xScale, yScale);
c.setScaleX(scale);
c.setScaleY(scale);
} else {
c.setScaleX(1d);
c.setScaleY(1d);
}
});
}
}
private void init() {
widthProperty().addListener((b, o, n) -> rescale());
heightProperty().addListener((b, o, n) -> rescale());
}
/**
* No argument constructor required for Externalizable (need this to work
* with SceneBuilder).
*/
public AutoScalingStackPane() {
super();
init();
}
/**
* Convenience constructor that takes a content Node.
*
* @param content The content of this pane
*/
public AutoScalingStackPane(Node content) {
super(content);
init();
}
/**
* AutoScale scaling options:
* {@link AutoScale#NONE}, {@link AutoScale#FILL}, {@link AutoScale#FIT}
*/
public enum AutoScale {
/**
* No scaling - revert to behaviour of <code>StackPane</code>.
*/
NONE,
/**
* Independently scaling in x and y so content fills whole region.
*/
FILL,
/**
* Scale preserving content aspect ratio and center in available space.
*/
FIT
}
// AutoScale Property
private ObjectProperty<AutoScale> autoScale =
new SimpleObjectProperty<AutoScale>(this, "autoScale", AutoScale.FIT);
/**
* AutoScalingStackPane scaling property
*
* @return AutoScalingStackPane scaling property
* @see AutoScale
*/
public ObjectProperty<AutoScale> autoScaleProperty() {
return autoScale;
}
/**
* Get AutoScale option
*
* @return the AutoScale option
* @see AutoScale
*/
public AutoScale getAutoScale() {
return autoScale.getValue();
}
/**
* Set the AutoScale option
*
* @param newAutoScale the AutoScale option to use
* @see AutoScale
*
*/
public void setAutoScale(AutoScale newAutoScale) {
autoScale.setValue(newAutoScale);
}
}