// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.tools;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.util.function.Function;
/**
* This is an extension of the flow layout that preferes wrapping the text instead of increasing the component width
* when there is not enough space.
* <p>
* This allows for a better preffered size computation.
* It should be used in all places where a flow layout fills the full width of the parent container.
* <p>
* This does not support baseline alignment.
* @author Michael Zangl
* @since 10622
*/
public class MultiLineFlowLayout extends FlowLayout {
/**
* Same as {@link FlowLayout#FlowLayout()}
*/
public MultiLineFlowLayout() {
super();
}
/**
* Same as {@link FlowLayout#FlowLayout(int, int, int)}
* @param align Alignment
* @param hgap horizontal gap
* @param vgap vertical gap
*/
public MultiLineFlowLayout(int align, int hgap, int vgap) {
super(align, hgap, vgap);
}
/**
* Same as {@link FlowLayout#FlowLayout(int)}
* @param align Alignment
*/
public MultiLineFlowLayout(int align) {
super(align);
}
@Override
public Dimension preferredLayoutSize(Container target) {
return getLayoutSize(target, Component::getPreferredSize);
}
@Override
public Dimension minimumLayoutSize(Container target) {
return getLayoutSize(target, Component::getMinimumSize);
}
private Dimension getLayoutSize(Container target, Function<Component, Dimension> baseSize) {
synchronized (target.getTreeLock()) {
int outerWidth = getWidthOf(target);
Insets insets = target.getInsets();
int containerWidth = outerWidth - insets.left - insets.right - getHgap() * 2;
int x = 0;
int totalHeight = insets.top + insets.bottom + getVgap() * 2;
int rowHeight = 0;
for (int i = 0; i < target.getComponentCount(); i++) {
Component child = target.getComponent(i);
if (!child.isVisible()) {
continue;
}
Dimension size = baseSize.apply(child);
if (x != 0) {
x += getHgap();
}
x += size.width;
if (x > containerWidth) {
totalHeight += rowHeight + getVgap();
rowHeight = 0;
x = 0;
}
rowHeight = Math.max(rowHeight, size.height);
}
totalHeight += rowHeight;
return new Dimension(outerWidth, totalHeight);
}
}
private static int getWidthOf(Container target) {
Container current = target;
while (current.getWidth() == 0 && current.getParent() != null) {
current = current.getParent();
}
int width = current.getWidth();
if (width == 0) {
return Integer.MAX_VALUE;
} else {
return width;
}
}
@Override
public String toString() {
return "MultiLineFlowLayout [align=" + getAlignment() + ']';
}
}