/*******************************************************************************
* Copyright (c) 2015, 2017 itemis AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthias Wienand (itemis AG) - initial API and implementation
*
*******************************************************************************/
package org.eclipse.gef.mvc.fx.policies;
import org.eclipse.gef.geometry.planar.Dimension;
import org.eclipse.gef.mvc.fx.operations.ForwardUndoCompositeOperation;
import org.eclipse.gef.mvc.fx.operations.ITransactionalOperation;
import org.eclipse.gef.mvc.fx.operations.ResizeContentOperation;
import org.eclipse.gef.mvc.fx.operations.ResizeOperation;
import org.eclipse.gef.mvc.fx.parts.IResizableContentPart;
import org.eclipse.gef.mvc.fx.parts.IVisualPart;
import javafx.geometry.Orientation;
import javafx.scene.Node;
/**
* The {@link ResizePolicy} is an {@link AbstractPolicy} that handles
* the resize of an {@link IVisualPart}.
*
* @author mwienand
*
*/
// TODO: respect max width and height
public class ResizePolicy extends AbstractPolicy {
/**
* Apply the new size to the host.
*
* @param dw
* The width delta.
* @param dh
* The height delta.
*/
protected void applySize(double dw, double dh) {
updateResizeOperation(dw, dh);
locallyExecuteOperation();
}
private double bounded(double min, double val, double max) {
if (max < min) {
double t = min;
min = max;
max = t;
}
return Math.max(min, Math.min(max, val));
}
@Override
public ITransactionalOperation commit() {
// XXX: super.commit() nulls the operation so we cache it in order to
// pass it along to #createResizeContentOperation().
ResizeOperation resizeOperation = getResizeOperation();
ITransactionalOperation commitOperation = super.commit();
if (commitOperation != null && !commitOperation.isNoOp()
&& isContentResizable()) {
// chain content changes
ForwardUndoCompositeOperation composite = new ForwardUndoCompositeOperation(
"Resize Content");
composite.add(commitOperation);
composite.add(createResizeContentOperation(resizeOperation));
commitOperation = composite;
}
return commitOperation;
}
/**
* Computes the applicable delta from the given delta width and delta height
* values, i.e. respecting the part's minimum size.
*
* @param dw
* The width delta.
* @param dh
* The height delta.
* @return A {@link Dimension} containing the applicable delta based on the
* given values.
*/
protected Dimension computeApplicableDelta(double dw, double dh) {
if (dw != 0 || dh != 0) {
// ensure visual is not resized below threshold
Node visual = getHost().getVisual();
Dimension initialSize = getInitialSize();
Dimension intendedSize = initialSize.getExpanded(dw, dh);
// determine final size based on content-bias, min-size, and
// max-size
Orientation contentBias = visual.getContentBias();
double w, h;
if (contentBias == null) {
w = bounded(visual.minWidth(-1), intendedSize.width,
visual.maxWidth(-1));
h = bounded(visual.minHeight(-1), intendedSize.height,
visual.maxHeight(-1));
} else if (contentBias == Orientation.HORIZONTAL) {
w = bounded(visual.minWidth(-1), intendedSize.width,
visual.maxWidth(-1));
h = bounded(visual.minHeight(w), intendedSize.height,
visual.maxHeight(w));
} else {
h = bounded(visual.minHeight(-1), intendedSize.height,
visual.maxHeight(-1));
w = bounded(visual.minWidth(h), intendedSize.width,
visual.maxWidth(h));
}
// adjust deltas
dw = w - initialSize.width;
dh = h - initialSize.height;
}
return new Dimension(dw, dh);
}
@Override
protected ITransactionalOperation createOperation() {
return new ResizeOperation("Resize", getHost(), getCurrentSize(), 0, 0);
}
/**
* Create an operation to resize the content according to the given
* {@link ResizeOperation}.
*
* @param resizeOperation
* The {@link ResizeOperation} for which to create a
* {@link ResizeContentOperation}.
* @return The operation to resize the content.
*/
protected ITransactionalOperation createResizeContentOperation(
ResizeOperation resizeOperation) {
Dimension initialSize = resizeOperation.getInitialSize();
Dimension finalSize = new Dimension(
initialSize.getWidth() + resizeOperation.getDw(),
initialSize.getHeight() + resizeOperation.getDh());
ResizeContentOperation<Node> resizeContentOperation = new ResizeContentOperation<>(
resizeOperation.getResizablePart(), initialSize, finalSize);
return resizeContentOperation;
}
/**
* Returns the current size of the {@link IResizableContentPart}.
*
* @return The current size.
*/
protected Dimension getCurrentSize() {
return getHost().getVisualSize();
}
/**
* Returns the delta height of the {@link #getResizeOperation() resize
* operation} that is used by this policy.
*
* @return The delta height of the {@link #getResizeOperation() resize
* operation} that is used by this policy.
*/
public double getDeltaHeight() {
return getResizeOperation().getDh();
}
/**
* Returns the delta width of the {@link #getResizeOperation() resize
* operation} that is used by this policy.
*
* @return The delta width of the {@link #getResizeOperation() resize
* operation} that is used by this policy.
*/
public double getDeltaWidth() {
return getResizeOperation().getDw();
}
@Override
public IResizableContentPart<? extends Node> getHost() {
return (IResizableContentPart<? extends Node>) super.getHost();
}
/**
* Returns the initial size of the {@link IResizableContentPart}.
*
* @return The initial size.
*/
protected Dimension getInitialSize() {
return getResizeOperation().getInitialSize();
}
/**
* Returns the {@link ResizeOperation} that is used by this
* {@link ResizePolicy}.
*
* @return The {@link ResizeOperation} used by this
* {@link AbstractPolicy}.
*/
protected ResizeOperation getResizeOperation() {
return (ResizeOperation) getOperation();
}
/**
* Returns whether the content part supports a content resize operation.
*
* @return <code>true</code> if content resize is supported,
* <code>false</code> otherwise.
*/
protected boolean isContentResizable() {
return getHost() instanceof IResizableContentPart;
}
/**
* Resizes the host by the given delta width and delta height.
*
* @param finalDw
* The delta width.
* @param finalDh
* The delta height.
*/
public void resize(double finalDw, double finalDh) {
checkInitialized();
applySize(finalDw, finalDh);
}
/**
* Computes the applicable delta width and height from the given intended
* delta values and updates the operation accordingly.
*
* @param intendedDeltaWidth
* The intended width delta.
* @param intendedDeltaHeight
* The intended height delta.
*/
protected void updateResizeOperation(double intendedDeltaWidth,
double intendedDeltaHeight) {
Dimension applicableDelta = computeApplicableDelta(intendedDeltaWidth,
intendedDeltaHeight);
ResizeOperation op = getResizeOperation();
op.setDw(applicableDelta.width);
op.setDh(applicableDelta.height);
}
}