Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.cleanroommc.modularui.api.layout;

import com.cleanroommc.modularui.api.widget.IWidget;

/**
* This is responsible for laying out widgets.
*/
Expand All @@ -17,4 +19,14 @@ public interface ILayoutWidget {
* The last call guarantees, that this widget is fully calculated.
*/
default void postLayoutWidgets() {}

/**
* Called when determining wrapping size of this widget.
* If this method returns true, size and margin of the queried child will be ignored for calculation.
* Typically return true when the child is disabled and you want to collapse it for layout.
* This method should also be used for layouting children with {@link #layoutWidgets} if it might return true.
*/
default boolean shouldIgnoreChildSize(IWidget child) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public static boolean isInside(int x, int y, int w, int h, int px, int py) {

public static final Area SHARED = new Area();

public static final Area ZERO = new Area();

/**
* relative position (in most cases the direct parent)
*/
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/cleanroommc/modularui/widget/sizer/Box.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public class Box {

public static final Box SHARED = new Box();

public static final Box ZERO = new Box();

public int left;
public int top;
public int right;
Expand Down Expand Up @@ -74,4 +76,14 @@ public int getStart(GuiAxis axis) {
public int getEnd(GuiAxis axis) {
return axis.isHorizontal() ? this.right : this.bottom;
}

@Override
public String toString() {
return "Box{" +
"left=" + left +
", top=" + top +
", right=" + right +
", bottom=" + bottom +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,10 @@ private void coverChildrenForLayout(IWidget widget) {
int x1 = Integer.MIN_VALUE, y1 = Integer.MIN_VALUE;
int w = 0, h = 0;
for (IWidget child : children) {
Box margin = child.getArea().getMargin();
final boolean shouldIgnoreChildSize = ((ILayoutWidget) this.parent).shouldIgnoreChildSize(child);
Box margin = shouldIgnoreChildSize ? Box.ZERO : child.getArea().getMargin();
IResizeable resizeable = child.resizer();
Area area = child.getArea();
Area area = shouldIgnoreChildSize ? Area.ZERO : child.getArea();
if (this.x.dependsOnChildren() && resizeable.isWidthCalculated()) {
w = Math.max(w, area.requestedWidth() + padding.horizontal());
if (resizeable.isXCalculated()) {
Expand Down
17 changes: 15 additions & 2 deletions src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
import com.cleanroommc.modularui.screen.viewport.ModularGuiContext;
import com.cleanroommc.modularui.theme.WidgetTheme;
import com.cleanroommc.modularui.widget.AbstractScrollWidget;
import com.cleanroommc.modularui.widget.scroll.HorizontalScrollData;
import com.cleanroommc.modularui.widget.scroll.ScrollData;
import com.cleanroommc.modularui.widget.scroll.VerticalScrollData;

import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import org.jetbrains.annotations.Nullable;

import java.util.function.IntFunction;

Expand All @@ -29,6 +27,7 @@ public class ListWidget<I extends IWidget, W extends ListWidget<I, W>> extends A
private ScrollData scrollData;
private IIcon childSeparator;
private final IntList separatorPositions = new IntArrayList();
private boolean collapseDisabledChild = false;

public ListWidget() {
super(null, null);
Expand Down Expand Up @@ -70,6 +69,7 @@ public void layoutWidgets() {
int separatorSize = getSeparatorSize();
int p = getArea().getPadding().getStart(axis);
for (IWidget widget : getChildren()) {
if (shouldIgnoreChildSize(widget)) continue;
if (axis.isVertical() ?
widget.getFlex().hasYPos() || !widget.resizer().isHeightCalculated() :
widget.getFlex().hasXPos() || !widget.resizer().isWidthCalculated()) {
Expand All @@ -89,6 +89,11 @@ public void layoutWidgets() {
getScrollData().setScrollSize(p + getArea().getPadding().getEnd(axis));
}

@Override
public boolean shouldIgnoreChildSize(IWidget child) {
return this.collapseDisabledChild && !child.isEnabled();
}

@Override
public boolean addChild(I child, int index) {
return super.addChild(child, index);
Expand Down Expand Up @@ -148,4 +153,12 @@ public W children(int amount, IntFunction<I> widgetCreator) {
}
return getThis();
}

/**
* Configures this widget to collapse disabled child widgets.
*/
public W collapseDisabledChild() {
this.collapseDisabledChild = true;
return getThis();
}
}
82 changes: 51 additions & 31 deletions src/main/java/com/cleanroommc/modularui/widgets/layout/Flow.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.cleanroommc.modularui.api.layout.ILayoutWidget;
import com.cleanroommc.modularui.api.widget.IWidget;
import com.cleanroommc.modularui.utils.Alignment;
import com.cleanroommc.modularui.widget.AbstractParentWidget;
import com.cleanroommc.modularui.widget.ParentWidget;
import com.cleanroommc.modularui.widget.sizer.Box;

Expand Down Expand Up @@ -35,6 +34,10 @@ public static Flow column() {
* Does not work with {@link Alignment.MainAxis#SPACE_BETWEEN} and {@link Alignment.MainAxis#SPACE_AROUND}.
*/
private int spaceBetween = 0;
/**
* Whether disabled child widgets should be collapsed for display.
*/
private boolean collapseDisabledChild = false;

public Flow(GuiAxis axis) {
this.axis = axis;
Expand All @@ -44,52 +47,54 @@ public Flow(GuiAxis axis) {
@Override
public void layoutWidgets() {
if (!hasChildren()) return;
boolean hasSize = resizer().isSizeCalculated(this.axis);
int size = getArea().getSize(axis);
Box padding = getArea().getPadding();
final boolean hasSize = resizer().isSizeCalculated(this.axis);
final Box padding = getArea().getPadding();
final int size = getArea().getSize(axis) - padding.getTotal(this.axis);
Alignment.MainAxis maa = this.maa;
if (!hasSize && maa != Alignment.MainAxis.START) {
if (flex().dependsOnChildren(axis)) {
maa = Alignment.MainAxis.START;
} else {
throw new IllegalStateException("Alignment.MainAxis other than start need the size to be calculated!");
}
}
if (maa == Alignment.MainAxis.SPACE_BETWEEN && getChildren().size() == 1) {
maa = Alignment.MainAxis.CENTER;
maa = Alignment.MainAxis.START;
}
int space = this.spaceBetween;

int totalSize = 0;
int childrenSize = 0;
int expandedAmount = 0;
int amount = 0;

// calculate total size and maximum width
// calculate total size
for (IWidget widget : getChildren()) {
// exclude self positioned (Y) children
// ignore disabled child if configured as such
if (shouldIgnoreChildSize(widget)) continue;
// exclude children whose position of main axis is fixed
if (widget.flex().hasPos(this.axis)) continue;
amount++;
if (widget.flex().isExpanded()) {
expandedAmount++;
totalSize += widget.getArea().getMargin().getTotal(this.axis);
childrenSize += widget.getArea().getMargin().getTotal(this.axis);
continue;
}
totalSize += widget.getArea().requestedSize(this.axis);
childrenSize += widget.getArea().requestedSize(this.axis);
}

if (amount <= 1 && maa == Alignment.MainAxis.SPACE_BETWEEN) {
maa = Alignment.MainAxis.CENTER;
}
final int spaceCount = Math.max(amount - 1, 0);

if (maa == Alignment.MainAxis.SPACE_BETWEEN || maa == Alignment.MainAxis.SPACE_AROUND) {
if (expandedAmount > 0) {
maa = Alignment.MainAxis.START;
} else {
space = 0;
}
}
totalSize += space * (getChildren().size() - 1);
childrenSize += space * spaceCount;

if (expandedAmount > 0 && hasSize) {
int newSize = (size - totalSize - padding.getTotal(this.axis)) / expandedAmount;
int newSize = (size - childrenSize) / expandedAmount;
for (IWidget widget : getChildren()) {
// exclude self positioned (Y) children
// ignore disabled child if configured as such
if (shouldIgnoreChildSize(widget)) continue;
// exclude children whose position of main axis is fixed
if (widget.flex().hasPos(this.axis)) continue;
if (widget.flex().isExpanded()) {
widget.getArea().setSize(this.axis, newSize);
Expand All @@ -102,25 +107,27 @@ public void layoutWidgets() {
int lastP = padding.getStart(this.axis);
if (hasSize) {
if (maa == Alignment.MainAxis.CENTER) {
lastP = (int) (size / 2f - totalSize / 2f);
lastP += (int) (size / 2f - childrenSize / 2f);
} else if (maa == Alignment.MainAxis.END) {
lastP = size - totalSize;
lastP += size - childrenSize;
}
}

for (IWidget widget : getChildren()) {
// exclude self positioned (Y) children
// ignore disabled child if configured as such
if (shouldIgnoreChildSize(widget)) continue;
// exclude children whose position of main axis is fixed
if (widget.flex().hasPos(this.axis)) continue;
Box margin = widget.getArea().getMargin();

// set calculated relative Y pos and set bottom margin for next widget
// set calculated relative main axis pos and set end margin for next widget
widget.getArea().setRelativePoint(this.axis, lastP + margin.getStart(this.axis));
widget.resizer().setPosResized(this.axis, true);
widget.resizer().setMarginPaddingApplied(this.axis, true);

lastP += widget.getArea().requestedSize(this.axis) + space;
if (hasSize && maa == Alignment.MainAxis.SPACE_BETWEEN) {
lastP += (size - totalSize) / (getChildren().size() - 1);
lastP += (size - childrenSize) / spaceCount;
}
}
}
Expand All @@ -132,26 +139,31 @@ public void postLayoutWidgets() {
Box padding = getArea().getPadding();
boolean hasWidth = resizer().isSizeCalculated(other);
for (IWidget widget : getChildren()) {
// exclude self positioned (Y) children
// exclude children whose position of main axis is fixed
if (widget.flex().hasPos(this.axis)) continue;
Box margin = widget.getArea().getMargin();
// don't align auto positioned (X) children in X
// don't align auto positioned children in cross axis
if (!widget.flex().hasPos(other) && widget.resizer().isSizeCalculated(other)) {
int x = margin.getStart(other) + padding.getStart(other);
int crossAxisPos = margin.getStart(other) + padding.getStart(other);
if (hasWidth) {
if (this.caa == Alignment.CrossAxis.CENTER) {
x = (int) (width / 2f - widget.getArea().getSize(other) / 2f);
crossAxisPos = (int) (width / 2f - widget.getArea().getSize(other) / 2f);
} else if (this.caa == Alignment.CrossAxis.END) {
x = width - widget.getArea().getSize(other) - margin.getEnd(other) - padding.getStart(other);
crossAxisPos = width - widget.getArea().getSize(other) - margin.getEnd(other) - padding.getStart(other);
}
}
widget.getArea().setRelativePoint(other, x);
widget.getArea().setRelativePoint(other, crossAxisPos);
widget.resizer().setPosResized(other, true);
widget.resizer().setMarginPaddingApplied(other, true);
}
}
}

@Override
public boolean shouldIgnoreChildSize(IWidget child) {
return this.collapseDisabledChild && !child.isEnabled();
}

public Flow crossAxisAlignment(Alignment.CrossAxis caa) {
this.caa = caa;
return this;
Expand All @@ -167,6 +179,14 @@ public Flow childPadding(int spaceBetween) {
return this;
}

/**
* Configures this widget to collapse disabled child widgets.
*/
public Flow collapseDisabledChild() {
this.collapseDisabledChild = true;
return this;
}

public GuiAxis getAxis() {
return axis;
}
Expand Down
20 changes: 17 additions & 3 deletions src/main/java/com/cleanroommc/modularui/widgets/layout/Grid.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class Grid extends AbstractScrollWidget<IWidget, Grid> implements ILayout
private int minRowHeight = 5, minColWidth = 5;
private Alignment alignment = Alignment.Center;
private boolean dirty = false;
private boolean collapseDisabledChild = false;

public Grid() {
super(null, null);
Expand Down Expand Up @@ -67,7 +68,7 @@ public void layoutWidgets() {
if (i == 0) {
colSizes.add(this.minColWidth);
}
if (child != null && child.isEnabled()) {
if (!shouldIgnoreChildSize(child)) {
rowSizes.set(i, Math.max(rowSizes.getInt(i), getElementHeight(child.getArea())));
colSizes.set(j, Math.max(colSizes.getInt(j), getElementWidth(child.getArea())));
}
Expand Down Expand Up @@ -100,6 +101,11 @@ public void layoutWidgets() {
}
}

@Override
public boolean shouldIgnoreChildSize(IWidget child) {
return child == null || (this.collapseDisabledChild && !child.isEnabled());
}

@Override
public @NotNull List<IWidget> getChildren() {
if (this.dirty) {
Expand All @@ -120,7 +126,7 @@ public int getDefaultHeight() {
for (List<IWidget> row : this.matrix) {
int rowHeight = 0;
for (IWidget child : row) {
if (child != null) {
if (!shouldIgnoreChildSize(child)) {
rowHeight = Math.max(rowHeight, getElementHeight(child.getArea()));
}
}
Expand All @@ -139,7 +145,7 @@ public int getDefaultWidth() {
if (i == 0) {
colSizes.add(this.minColWidth);
}
if (child != null) {
if (!shouldIgnoreChildSize(child)) {
colSizes.set(j, Math.max(colSizes.getInt(j), getElementWidth(child.getArea())));
}
j++;
Expand Down Expand Up @@ -282,6 +288,14 @@ public Grid minElementMarginBottom(int val) {
return getThis();
}

/**
* Configures this widget to collapse row/column if all the child widgets in that axis are disabled.
*/
public Grid collapseDisabledChild() {
this.collapseDisabledChild = true;
return getThis();
}

public static <T, I extends IWidget> List<List<I>> mapToMatrix(int rowLength, List<T> list, IndexedElementMapper<T, I> widgetCreator) {
return mapToMatrix(rowLength, list.size(), i -> widgetCreator.apply(i, list.get(i)));
}
Expand Down