package at.bestsolution.efxclipse.runtime.panels;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.scene.Node;
public class RowLayoutPane extends AbstractLayoutPane<RowLayoutPane.RowData> {
public static class RowData {
/**
* width specifies the desired width in pixels. This value is the wHint
* passed into Control.computeSize(int, int, boolean) to determine the
* preferred size of the control.
*
* The default value is SWT.DEFAULT.
*
* @see org.eclipse.swt.widgets.Control#computeSize(int, int, boolean)
*/
public int width = SWT_DEFAULT;
/**
* height specifies the preferred height in pixels. This value is the
* hHint passed into Control.computeSize(int, int, boolean) to determine
* the preferred size of the control.
*
* The default value is SWT.DEFAULT.
*
* @see org.eclipse.swt.widgets.Control#computeSize(int, int, boolean)
*/
public int height = SWT_DEFAULT;
/**
* exclude informs the layout to ignore this control when sizing and
* positioning controls. If this value is <code>true</code>, the size
* and position of the control will not be managed by the layout. If
* this value is <code>false</code>, the size and position of the
* control will be computed and assigned.
*
* The default value is <code>false</code>.
*
* @since 3.1
*/
public boolean exclude = false;
/**
* Constructs a new instance of RowData using default values.
*/
public RowData() {
}
/**
* Constructs a new instance of RowData according to the parameters. A
* value of SWT.DEFAULT indicates that no minimum width or no minimum
* height is specified.
*
* @param width
* a minimum width for the control
* @param height
* a minimum height for the control
*/
public RowData(int width, int height) {
this.width = width;
this.height = height;
}
String getName() {
String string = getClass().getName();
int index = string.lastIndexOf('.');
if (index == -1)
return string;
return string.substring(index + 1, string.length());
}
/**
* Returns a string containing a concise, human-readable description of
* the receiver.
*
* @return a string representation of the RowData object
*/
public String toString() {
String string = getName() + " {";
if (width != SWT_DEFAULT)
string += "width=" + width + " ";
if (height != SWT_DEFAULT)
string += "height=" + height + " ";
if (exclude)
string += "exclude=" + exclude + " ";
string = string.trim();
string += "}";
return string;
}
}
/**
* type specifies whether the layout places controls in rows or columns.
*
* The default value is HORIZONTAL.
*
* Possible values are:
* <ul>
* <li>HORIZONTAL: Position the controls horizontally from left to right</li>
* <li>VERTICAL: Position the controls vertically from top to bottom</li>
* </ul>
*
* @since 2.0
*/
public int type = SWT_HORIZONTAL;
/**
* marginWidth specifies the number of pixels of horizontal margin that will
* be placed along the left and right edges of the layout.
*
* The default value is 0.
*
* @since 3.0
*/
public int marginWidth = 0;
/**
* marginHeight specifies the number of pixels of vertical margin that will
* be placed along the top and bottom edges of the layout.
*
* The default value is 0.
*
* @since 3.0
*/
public int marginHeight = 0;
/**
* spacing specifies the number of pixels between the edge of one cell and
* the edge of its neighbouring cell.
*
* The default value is 3.
*/
public int spacing = 3;
/**
* wrap specifies whether a control will be wrapped to the next row if there
* is insufficient space on the current row.
*
* The default value is true.
*/
public boolean wrap = true;
/**
* pack specifies whether all controls in the layout take their preferred
* size. If pack is false, all controls will have the same size which is the
* size required to accommodate the largest preferred height and the largest
* preferred width of all the controls in the layout.
*
* The default value is true.
*/
public boolean pack = true;
/**
* fill specifies whether the controls in a row should be all the same
* height for horizontal layouts, or the same width for vertical layouts.
*
* The default value is false.
*
* @since 3.0
*/
public boolean fill = false;
/**
* center specifies whether the controls in a row should be centered
* vertically in each cell for horizontal layouts, or centered horizontally
* in each cell for vertical layouts.
*
* The default value is false.
*
* @since 3.4
*/
public boolean center = false;
/**
* justify specifies whether the controls in a row should be fully
* justified, with any extra space placed between the controls.
*
* The default value is false.
*/
public boolean justify = false;
/**
* marginLeft specifies the number of pixels of horizontal margin that will
* be placed along the left edge of the layout.
*
* The default value is 3.
*/
public int marginLeft = 3;
/**
* marginTop specifies the number of pixels of vertical margin that will be
* placed along the top edge of the layout.
*
* The default value is 3.
*/
public int marginTop = 3;
/**
* marginRight specifies the number of pixels of horizontal margin that will
* be placed along the right edge of the layout.
*
* The default value is 3.
*/
public int marginRight = 3;
/**
* marginBottom specifies the number of pixels of vertical margin that will
* be placed along the bottom edge of the layout.
*
* The default value is 3.
*/
public int marginBottom = 3;
@Override
protected at.bestsolution.efxclipse.runtime.panels.AbstractLayoutPane.Size computeSize(
double wHint, double hHint, boolean flushCache) {
Size extent;
if (type == SWT_HORIZONTAL) {
extent = layoutHorizontal(false, (wHint != SWT_DEFAULT) && wrap,
wHint, flushCache);
} else {
extent = layoutVertical(false, (hHint != SWT_DEFAULT) && wrap,
hHint, flushCache);
}
double width = extent.width;
double height = extent.height;
if (wHint != SWT_DEFAULT)
width = wHint;
if (hHint != SWT_DEFAULT)
height = hHint;
return new Size(width,height);
}
Size computeSize(Node control, boolean flushCache) {
int wHint = SWT_DEFAULT, hHint = SWT_DEFAULT;
RowData data = (RowData) getConstraint(control);
if (data != null) {
wHint = data.width;
hHint = data.height;
}
return new Size(control.prefWidth(wHint), control.prefHeight(hHint));
}
@Override
protected void layoutChildren() {
super.layoutChildren();
Bounds clientArea = getLayoutBounds();
if (type == SWT_HORIZONTAL) {
layoutHorizontal(true, wrap, clientArea.getWidth(), true);
} else {
layoutVertical(true, wrap, clientArea.getHeight(), true);
}
}
Size layoutHorizontal(boolean move, boolean wrap, double width,
boolean flushCache) {
Node[] children = getChildren().toArray(new Node[0]);
int count = 0;
for (int i = 0; i < children.length; i++) {
Node control = children[i];
RowData data = (RowData) getConstraint(control);
if (data == null || !data.exclude) {
children[count++] = children[i];
}
}
if (count == 0) {
return new Size(marginLeft + marginWidth * 2 + marginRight,
marginTop + marginHeight * 2 + marginBottom);
}
double childWidth = 0, childHeight = 0, maxHeight = 0;
if (!pack) {
for (int i = 0; i < count; i++) {
Node child = children[i];
Size size = computeSize(child, flushCache);
childWidth = Math.max(childWidth, size.width);
childHeight = Math.max(childHeight, size.height);
}
maxHeight = childHeight;
}
double clientX = 0, clientY = 0;
if (move) {
Bounds rect = getLayoutBounds();
clientX = rect.getMinX();
clientY = rect.getMinY();
}
double[] wraps = null;
boolean wrapped = false;
Bounds[] bounds = null;
if (move && (justify || fill || center)) {
bounds = new Bounds[count];
wraps = new double[count];
}
double maxX = 0, x = marginLeft + marginWidth, y = marginTop
+ marginHeight;
for (int i = 0; i < count; i++) {
Node child = children[i];
if (pack) {
Size size = computeSize(child, flushCache);
childWidth = size.width;
childHeight = size.height;
}
if (wrap && (i != 0) && (x + childWidth > width)) {
wrapped = true;
if (move && (justify || fill || center))
wraps[i - 1] = maxHeight;
x = marginLeft + marginWidth;
y += spacing + maxHeight;
if (pack)
maxHeight = 0;
}
if (pack || fill || center) {
maxHeight = Math.max(maxHeight, childHeight);
}
if (move) {
double childX = x + clientX, childY = y + clientY;
if (justify || fill || center) {
bounds[i] = new BoundingBox(childX, childY, childWidth,
childHeight);
} else {
child.resizeRelocate(childX, childY, childWidth,
childHeight);
}
}
x += spacing + childWidth;
maxX = Math.max(maxX, x);
}
maxX = Math.max(clientX + marginLeft + marginWidth, maxX - spacing);
if (!wrapped)
maxX += marginRight + marginWidth;
if (move && (justify || fill || center)) {
double space = 0, margin = 0;
if (!wrapped) {
space = Math.max(0, (width - maxX) / (count + 1));
margin = Math.max(0, ((width - maxX) % (count + 1)) / 2);
} else {
if (fill || justify || center) {
int last = 0;
if (count > 0)
wraps[count - 1] = maxHeight;
for (int i = 0; i < count; i++) {
if (wraps[i] != 0) {
int wrapCount = i - last + 1;
if (justify) {
int wrapX = 0;
for (int j = last; j <= i; j++) {
wrapX += bounds[j].getWidth() + spacing;
}
space = Math.max(0, (width - wrapX)
/ (wrapCount + 1));
margin = Math
.max(0,
((width - wrapX) % (wrapCount + 1)) / 2);
}
for (int j = last; j <= i; j++) {
if (justify)
bounds[j] = changeX(bounds[j], (space * (j
- last + 1))
+ margin);
if (fill) {
bounds[j] = changeHeight(bounds[j],
wraps[i]);
} else {
if (center) {
bounds[j] = changeY(
bounds[j],
Math.max(
0,
(wraps[i] - bounds[j]
.getHeight()) / 2));
}
}
}
last = i + 1;
}
}
}
}
for (int i = 0; i < count; i++) {
if (!wrapped) {
if (justify)
bounds[i] = changeX(bounds[i], (space * (i + 1))
+ margin);
if (fill) {
bounds[i] = changeHeight(bounds[i], maxHeight);
} else {
if (center) {
bounds[i] = changeY(bounds[i], Math.max(0,
(maxHeight - bounds[i].getHeight()) / 2));
}
}
}
children[i].resizeRelocate(bounds[i].getMinX(),
bounds[i].getMinY(), bounds[i].getWidth(),
bounds[i].getHeight());
}
}
return new Size(maxX, y + maxHeight + marginBottom + marginHeight);
}
Size layoutVertical (boolean move, boolean wrap, double height, boolean flushCache) {
Node [] children = getChildren ().toArray(new Node[0]);
int count = 0;
for (int i=0; i<children.length; i++) {
Node control = children [i];
RowData data = (RowData) getConstraint(control);
if (data == null || !data.exclude) {
children [count++] = children [i];
}
}
if (count == 0) {
return new Size (marginLeft + marginWidth * 2 + marginRight, marginTop + marginHeight * 2 + marginBottom);
}
double childWidth = 0, childHeight = 0, maxWidth = 0;
if (!pack) {
for (int i=0; i<count; i++) {
Node child = children [i];
Size size = computeSize (child, flushCache);
childWidth = Math.max (childWidth, size.width);
childHeight = Math.max (childHeight, size.height);
}
maxWidth = childWidth;
}
double clientX = 0, clientY = 0;
if (move) {
Bounds rect = getLayoutBounds();
clientX = rect.getMinX();
clientY = rect.getMinY();
}
double [] wraps = null;
boolean wrapped = false;
Bounds [] bounds = null;
if (move && (justify || fill || center)) {
bounds = new Bounds [count];
wraps = new double [count];
}
double maxY = 0, x = marginLeft + marginWidth, y = marginTop + marginHeight;
for (int i=0; i<count; i++) {
Node child = children [i];
if (pack) {
Size size = computeSize (child, flushCache);
childWidth = size.width;
childHeight = size.height;
}
if (wrap && (i != 0) && (y + childHeight > height)) {
wrapped = true;
if (move && (justify || fill || center)) wraps [i - 1] = maxWidth;
x += spacing + maxWidth;
y = marginTop + marginHeight;
if (pack) maxWidth = 0;
}
if (pack || fill || center) {
maxWidth = Math.max (maxWidth, childWidth);
}
if (move) {
double childX = x + clientX, childY = y + clientY;
if (justify || fill || center) {
bounds [i] = new BoundingBox(childX, childY, childWidth, childHeight);
} else {
child.resizeRelocate(childX, childY, childWidth, childHeight);
}
}
y += spacing + childHeight;
maxY = Math.max (maxY, y);
}
maxY = Math.max (clientY + marginTop + marginHeight, maxY - spacing);
if (!wrapped) maxY += marginBottom + marginHeight;
if (move && (justify || fill || center)) {
double space = 0, margin = 0;
if (!wrapped) {
space = Math.max (0, (height - maxY) / (count + 1));
margin = Math.max (0, ((height - maxY) % (count + 1)) / 2);
} else {
if (fill || justify || center) {
int last = 0;
if (count > 0) wraps [count - 1] = maxWidth;
for (int i=0; i<count; i++) {
if (wraps [i] != 0) {
int wrapCount = i - last + 1;
if (justify) {
int wrapY = 0;
for (int j=last; j<=i; j++) {
wrapY += bounds [j].getHeight() + spacing;
}
space = Math.max (0, (height - wrapY) / (wrapCount + 1));
margin = Math.max (0, ((height - wrapY) % (wrapCount + 1)) / 2);
}
for (int j=last; j<=i; j++) {
if (justify) bounds [j] = changeY(bounds [j], (space * (j - last + 1)) + margin);
if (fill) {
bounds [j] = changeWidth(bounds [j],wraps [i]);
} else {
if (center) {
bounds [j] = changeX(bounds [j],Math.max (0, (wraps [i] - bounds [j].getWidth()) / 2));
}
}
}
last = i + 1;
}
}
}
}
for (int i=0; i<count; i++) {
if (!wrapped) {
if (justify) bounds [i] = changeY(bounds [i],(space * (i + 1)) + margin);
if (fill) {
bounds [i] = changeWidth(bounds [i], maxWidth);
} else {
if (center) {
bounds [i] = changeX(bounds [i],Math.max (0, (maxWidth - bounds [i].getWidth()) / 2));
}
}
}
children [i].resizeRelocate(bounds [i].getMinX(),bounds [i].getMinY(), bounds [i].getWidth(), bounds [i].getHeight());
}
}
return new Size (x + maxWidth + marginRight + marginWidth, maxY);
}
private static Bounds changeX(Bounds original, double amount) {
return new BoundingBox(original.getMinX() + amount, original.getMinY(),
original.getWidth(), original.getHeight());
}
private static Bounds changeY(Bounds original, double amount) {
return new BoundingBox(original.getMinX(), original.getMinY() + amount,
original.getWidth(), original.getHeight());
}
private static Bounds changeHeight(Bounds original, double amount) {
return new BoundingBox(original.getMinX(), original.getMinY(),
original.getWidth(), original.getHeight() + amount);
}
private static Bounds changeWidth(Bounds original, double amount) {
return new BoundingBox(original.getMinX(), original.getMinY(),
original.getWidth() + amount, original.getHeight());
}
}