More flexibility for placement

This commit is contained in:
Frank 2022-07-15 00:56:45 +02:00
parent f89035f9f3
commit 665a9d2c27
8 changed files with 185 additions and 27 deletions

View file

@ -2,14 +2,19 @@ package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.input.MouseEvent;
import org.betterx.ui.layout.components.render.ComponentRenderer;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.DynamicSize;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.vertex.PoseStack;
public abstract class Component<R extends ComponentRenderer> implements ComponentWithBounds {
protected final R renderer;
protected final DynamicSize width;
protected final DynamicSize height;
protected Rectangle relativeBounds;
protected Alignment vAlign = Alignment.MIN;
protected Alignment hAlign = Alignment.MIN;
public Component(DynamicSize width, DynamicSize height, R renderer) {
this.width = width.attachComponent(this::getContentWidth);
@ -27,6 +32,10 @@ public abstract class Component<R extends ComponentRenderer> implements Componen
void setRelativeBounds(int left, int top) {
relativeBounds = new Rectangle(left, top, width.calculatedSize(), height.calculatedSize());
onBoundsChanged();
}
protected void onBoundsChanged() {
}
public Rectangle getRelativeBounds() {
@ -52,17 +61,20 @@ public abstract class Component<R extends ComponentRenderer> implements Componen
return height.calculatedSize();
}
public void render(Rectangle parentBounds, Rectangle clipRect) {
public void render(PoseStack poseStack, Rectangle parentBounds, Rectangle clipRect) {
Rectangle r = relativeBounds.movedBy(parentBounds.left, parentBounds.top);
Rectangle clip = r.intersect(clipRect);
poseStack.pushPose();
poseStack.translate(relativeBounds.left, relativeBounds.top, 0);
if (r.overlaps(clip)) {
renderInBounds(r, clip);
renderInBounds(poseStack, r, clip);
}
poseStack.popPose();
}
protected void renderInBounds(Rectangle renderBounds, Rectangle clipRect) {
protected void renderInBounds(PoseStack poseStack, Rectangle renderBounds, Rectangle clipRect) {
if (renderer != null) {
renderer.renderInBounds(renderBounds, clipRect);
renderer.renderInBounds(poseStack, renderBounds, clipRect);
}
}
@ -78,4 +90,34 @@ public abstract class Component<R extends ComponentRenderer> implements Componen
public String toString() {
return super.toString() + "(" + relativeBounds + ", " + width.calculatedSize() + "x" + height.calculatedSize() + ")";
}
public Component<R> alignTop() {
vAlign = Alignment.MIN;
return this;
}
public Component<R> alignBottom() {
vAlign = Alignment.MAX;
return this;
}
public Component<R> centerVertical() {
vAlign = Alignment.CENTER;
return this;
}
public Component<R> alignLeft() {
hAlign = Alignment.MIN;
return this;
}
public Component<R> alignRight() {
hAlign = Alignment.MAX;
return this;
}
public Component<R> centerHorizontal() {
hAlign = Alignment.CENTER;
return this;
}
}

View file

@ -1,8 +1,12 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.*;
import org.betterx.ui.layout.components.input.*;
import org.betterx.ui.layout.values.*;
import org.betterx.ui.layout.components.input.MouseEvent;
import org.betterx.ui.layout.components.render.ComponentRenderer;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.DynamicSize;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.vertex.PoseStack;
import java.util.LinkedList;
import java.util.List;
@ -36,8 +40,9 @@ public class HorizontalStack<R extends ComponentRenderer> extends Component<R> {
@Override
protected int updateContainerHeight(int containerHeight) {
int myHeight = height.calculateOrFill(containerHeight);
components.stream().forEach(c -> c.height.calculateOrFill(myHeight));
for (Component<?> c : components) {
c.updateContainerHeight(myHeight);
c.updateContainerHeight(c.height.calculatedSize());
}
return myHeight;
}
@ -49,7 +54,10 @@ public class HorizontalStack<R extends ComponentRenderer> extends Component<R> {
int offset = 0;
for (Component<?> c : components) {
c.setRelativeBounds(offset, 0);
int delta = relativeBounds.height - c.height.calculatedSize();
if (c.hAlign == Alignment.MIN) delta = 0;
else if (c.hAlign == Alignment.CENTER) delta /= 2;
c.setRelativeBounds(offset, delta);
offset += c.relativeBounds.width;
}
}
@ -89,10 +97,10 @@ public class HorizontalStack<R extends ComponentRenderer> extends Component<R> {
// }
@Override
protected void renderInBounds(Rectangle renderBounds, Rectangle clipRect) {
super.renderInBounds(renderBounds, clipRect);
protected void renderInBounds(PoseStack poseStack, Rectangle renderBounds, Rectangle clipRect) {
super.renderInBounds(poseStack, renderBounds, clipRect);
for (Component<?> c : components) {
c.render(renderBounds, clipRect);
c.render(poseStack, renderBounds, clipRect);
}
}
@ -105,8 +113,33 @@ public class HorizontalStack<R extends ComponentRenderer> extends Component<R> {
}
}
public static HorizontalStack<?> centered(Component<?> c) {
return new HorizontalStack<>(DynamicSize.relative(1), DynamicSize.relative(1)).addFiller().add(c).addFiller();
}
public static HorizontalStack<?> bottom(Component<?> c) {
return new HorizontalStack<>(DynamicSize.relative(1), DynamicSize.relative(1)).add(c).addFiller();
}
public HorizontalStack<R> add(Component<?> c) {
this.components.add(c);
return this;
}
private HorizontalStack<R> addEmpty(DynamicSize size) {
this.components.add(new Empty(size, DynamicSize.fixed(0)));
return this;
}
public HorizontalStack<R> addSpacer(int size) {
return addEmpty(DynamicSize.fixed(size));
}
public HorizontalStack<R> addSpacer(float percentage) {
return addEmpty(DynamicSize.relative(percentage));
}
public HorizontalStack<R> addFiller() {
return addEmpty(DynamicSize.fill());
}
}

View file

@ -6,6 +6,8 @@ import org.betterx.ui.layout.components.render.ScrollerRenderer;
import org.betterx.ui.layout.values.DynamicSize;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.vertex.PoseStack;
public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRenderer> extends Component<R> {
protected Component<?> child;
protected final RS scrollerRenderer;
@ -33,7 +35,8 @@ public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRend
protected int updateContainerWidth(int containerWidth) {
int myWidth = width.calculateOrFill(containerWidth);
if (child != null) {
child.updateContainerWidth(myWidth);
child.width.calculateOrFill(myWidth);
child.updateContainerWidth(child.width.calculatedSize());
}
return myWidth;
}
@ -42,7 +45,8 @@ public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRend
protected int updateContainerHeight(int containerHeight) {
int myHeight = height.calculateOrFill(containerHeight);
if (child != null) {
child.updateContainerHeight(myHeight);
child.height.calculateOrFill(myHeight);
child.updateContainerHeight(child.height.calculatedSize());
}
return myHeight;
}
@ -77,17 +81,21 @@ public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRend
}
@Override
protected void renderInBounds(Rectangle renderBounds, Rectangle clipRect) {
super.renderInBounds(renderBounds, clipRect);
protected void renderInBounds(PoseStack poseStack, Rectangle renderBounds, Rectangle clipRect) {
super.renderInBounds(poseStack, renderBounds, clipRect);
if (showScrollBar()) {
if (child != null) {
child.render(renderBounds.movedBy(0, scrollerOffset(), scrollerRenderer.scrollerWidth(), 0), clipRect);
child.render(
poseStack,
renderBounds.movedBy(0, scrollerOffset(), scrollerRenderer.scrollerWidth(), 0),
clipRect
);
}
scrollerRenderer.renderScrollBar(renderBounds, saveScrollerY(), scrollerHeight);
} else {
if (child != null) {
child.render(renderBounds, clipRect);
child.render(poseStack, renderBounds, clipRect);
}
}
}

View file

@ -3,9 +3,12 @@ package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.input.MouseEvent;
import org.betterx.ui.layout.components.render.ComponentRenderer;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.DynamicSize;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.vertex.PoseStack;
import java.util.LinkedList;
import java.util.List;
@ -23,8 +26,9 @@ public class VerticalStack<R extends ComponentRenderer> extends Component<R> {
@Override
protected int updateContainerWidth(int containerWidth) {
int myWidth = width.calculateOrFill(containerWidth);
components.stream().forEach(c -> c.width.calculateOrFill(myWidth));
for (Component<?> c : components) {
c.updateContainerWidth(myWidth);
c.updateContainerWidth(c.width.calculatedSize());
}
return myWidth;
}
@ -50,7 +54,10 @@ public class VerticalStack<R extends ComponentRenderer> extends Component<R> {
int offset = 0;
for (Component<?> c : components) {
c.setRelativeBounds(0, offset);
int delta = relativeBounds.width - c.width.calculatedSize();
if (c.hAlign == Alignment.MIN) delta = 0;
else if (c.hAlign == Alignment.CENTER) delta /= 2;
c.setRelativeBounds(delta, offset);
offset += c.relativeBounds.height;
}
}
@ -89,10 +96,10 @@ public class VerticalStack<R extends ComponentRenderer> extends Component<R> {
}
@Override
protected void renderInBounds(Rectangle renderBounds, Rectangle clipRect) {
super.renderInBounds(renderBounds, clipRect);
protected void renderInBounds(PoseStack poseStack, Rectangle renderBounds, Rectangle clipRect) {
super.renderInBounds(poseStack, renderBounds, clipRect);
for (Component<?> c : components) {
c.render(renderBounds, clipRect);
c.render(poseStack, renderBounds, clipRect);
}
}
@ -105,8 +112,33 @@ public class VerticalStack<R extends ComponentRenderer> extends Component<R> {
}
}
public static VerticalStack<?> centered(Component<?> c) {
return new VerticalStack<>(DynamicSize.relative(1), DynamicSize.relative(1)).addFiller().add(c).addFiller();
}
public static VerticalStack<?> bottom(Component<?> c) {
return new VerticalStack<>(DynamicSize.relative(1), DynamicSize.relative(1)).add(c).addFiller();
}
public VerticalStack<R> add(Component<?> c) {
this.components.add(c);
return this;
}
private VerticalStack<R> addEmpty(DynamicSize size) {
this.components.add(new Empty(DynamicSize.fixed(0), size));
return this;
}
public VerticalStack<R> addSpacer(int size) {
return addEmpty(DynamicSize.fixed(size));
}
public VerticalStack<R> addSpacer(float percentage) {
return addEmpty(DynamicSize.relative(percentage));
}
public VerticalStack<R> addFiller() {
return addEmpty(DynamicSize.fill());
}
}

View file

@ -2,6 +2,8 @@ package org.betterx.ui.layout.components.render;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.vertex.PoseStack;
public interface ComponentRenderer {
void renderInBounds(Rectangle bounds, Rectangle clipRect);
void renderInBounds(PoseStack stack, Rectangle bounds, Rectangle clipRect);
}

View file

@ -0,0 +1,5 @@
package org.betterx.ui.layout.values;
public enum Alignment {
MIN, MAX, CENTER
}

View file

@ -25,6 +25,10 @@ public class DynamicSize {
return new DynamicSize(SizeType.FIT_CONTENT);
}
public static DynamicSize fitOrFill() {
return new DynamicSize(SizeType.FIT_CONTENT_OR_FILL);
}
public int calculatedSize() {
return calculatedSize;
}
@ -67,7 +71,7 @@ public class DynamicSize {
public int calculateOrFill(int parentSize) {
calculatedSize = calculate(parentSize);
if (sizeType instanceof SizeType.Fill) {
if (sizeType instanceof SizeType.Fill || sizeType instanceof SizeType.FitContentOrFill) {
calculatedSize = parentSize;
}
@ -75,7 +79,7 @@ public class DynamicSize {
}
public double fillWeight() {
if (sizeType instanceof SizeType.Fill fill) {
if (sizeType instanceof SizeType.Fill fill || sizeType instanceof SizeType.FitContentOrFill) {
return 1;
}
return 0;

View file

@ -2,12 +2,34 @@ package org.betterx.ui.layout.values;
public interface SizeType {
FitContent FIT_CONTENT = new FitContent();
FitContentOrFill FIT_CONTENT_OR_FILL = new FitContentOrFill();
Fill FILL = new Fill();
record Fill() implements SizeType {
}
record FitContent(ContentSizeSupplier contentSize) implements SizeType {
class FitContentOrFill extends FitContent {
public FitContentOrFill(ContentSizeSupplier contentSize) {
super(contentSize);
}
public FitContentOrFill() {
super();
}
@Override
public FitContentOrFill copyForSupplier(ContentSizeSupplier component) {
return new FitContentOrFill(component);
}
}
class FitContent implements SizeType {
private final ContentSizeSupplier contentSize;
public FitContent(ContentSizeSupplier contentSize) {
this.contentSize = contentSize;
}
@FunctionalInterface
public interface ContentSizeSupplier {
int get();
@ -20,6 +42,16 @@ public interface SizeType {
public FitContent copyForSupplier(ContentSizeSupplier component) {
return new FitContent(component);
}
public ContentSizeSupplier contentSize() {
return contentSize;
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}
record Fixed(int size) implements SizeType {