More flexibility for placement
This commit is contained in:
parent
f89035f9f3
commit
665a9d2c27
8 changed files with 185 additions and 27 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package org.betterx.ui.layout.values;
|
||||
|
||||
public enum Alignment {
|
||||
MIN, MAX, CENTER
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue