New weighted constrained

This commit is contained in:
Speiger 2025-01-23 10:20:02 +01:00
parent bfe426f11e
commit dce550da0d
6 changed files with 194 additions and 18 deletions

View File

@ -18,10 +18,10 @@ import speiger.src.coreengine.math.vector.matrix.Matrix4f;
import speiger.src.coreengine.rendering.gui.font.Font;
import speiger.src.coreengine.rendering.gui.font.FontManager;
import speiger.src.coreengine.rendering.gui.font.glyth.Glyth;
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth;
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth.GlythBaker;
import speiger.src.coreengine.rendering.gui.font.glyth.IGlythSheetInfo;
import speiger.src.coreengine.rendering.gui.font.glyth.MissingGlyth;
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth;
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth.GlythBaker;
import speiger.src.coreengine.rendering.gui.font.providers.IFontProvider;
import speiger.src.coreengine.rendering.gui.font.providers.STBTrueTypeProvider;
import speiger.src.coreengine.rendering.input.devices.FileDrop;

View File

@ -5,6 +5,7 @@ import java.util.function.Consumer;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.rendering.gui.animation.GuiAnimator;
import speiger.src.coreengine.rendering.gui.layout.constraints.ConstrainedContext;
import speiger.src.coreengine.rendering.gui.layout.constraints.ConstraintContainer;
import speiger.src.coreengine.rendering.gui.renderer.IUIRenderer;
import speiger.src.coreengine.rendering.guiOld.helper.box.IGuiBox;
@ -21,6 +22,7 @@ public abstract non-sealed class GuiComponent extends FlagObject implements ICas
final IGuiBox box;
IComponentScreen screen;
ConstraintContainer constraints;
ConstrainedContext constraintedContext = new ConstrainedContext();
GuiAnimator animator;
GuiComponent parent;
List<GuiComponent> children = new ObjectArrayList<>();
@ -102,12 +104,20 @@ public abstract non-sealed class GuiComponent extends FlagObject implements ICas
return true;
}
private ConstrainedContext parentContext() {
return parent != null ? parent.constraintedContext : null;
}
public GuiComponent withConstraints(ConstraintContainer container) {
this.constraints = container;
if((screen != null || parent != null) && constraints != null) constraints.apply(this, parent);
if((screen != null || parent != null) && constraints != null) constraints.apply(this, parent, parentContext());
return this;
}
public ConstraintContainer constraint() {
return constraints;
}
public GuiComponent addChild(GuiComponent child) {
if(child.parent != null) throw new IllegalArgumentException("A Child can not have multiple parents");
child.parent = this;
@ -155,13 +165,16 @@ public abstract non-sealed class GuiComponent extends FlagObject implements ICas
}
public void onChanged(boolean repaint) {
if(constraints != null) constraints.apply(this, parent);
if(constraints != null) constraints.apply(this, parent, parentContext());
if(animator != null) animator.apply();
box.onChanged();
constraintedContext.update(this, children);
notifyListeners(LISTENER_ON_CHANGE);
if(repaint) repaint();
if(children.isEmpty()) return;
int index = 0;
for(GuiComponent comp : children) {
constraintedContext.setCurrent(index++);
comp.onChanged(repaint);
}
}
@ -173,6 +186,7 @@ public abstract non-sealed class GuiComponent extends FlagObject implements ICas
public IGuiBox getBox() { return box; }
public IInteractable interactContainer() { return interactions; }
@Override
public GuiComponent set(float x, float y) {
if(box.getBaseX() != x || box.getBaseY() != y) {

View File

@ -0,0 +1,89 @@
package speiger.src.coreengine.rendering.gui.layout.constraints;
import java.util.List;
import speiger.src.coreengine.rendering.gui.components.base.GuiComponent;
import speiger.src.coreengine.rendering.guiOld.helper.box.IGuiBox;
public class ConstrainedContext {
float[] bounds;
float[] weights;
boolean locked = false;
int current;
public void update(GuiComponent owner, List<GuiComponent> children) {
locked = false;
bounds = new float[children.size()*2];
weights = new float[children.size()*2];
for(int i = 0,m=children.size();i<m;i++) {
current = i;
GuiComponent component = children.get(i);
if(component.constraint() == null) {
applyDefault(component);
continue;
}
component.constraint().fetch(component, owner, this);
}
locked = true;
float[] totals = new float[4];
for(int i = 0,m=children.size();i<m;i++) {
totals[0] += bounds[i*2];
totals[1] += bounds[i*2+1];
totals[2] += weights[i*2];
totals[3] += weights[i*2+1];
}
IGuiBox box = owner.getBox();
totals[0] -= box.getBaseWidth();
totals[1] -= box.getBaseHeight();
if(totals[2] > 0F) {
float scale = 1F / totals[2];
for(int i = 0,m=children.size();i<m;i++) {
float value = weights[i*2];
if(value <= 0F) continue;
bounds[i*2] = value * scale;
}
}
if(totals[3] > 0F) {
float scale = 1F / totals[3];
for(int i = 0,m=children.size();i<m;i++) {
float value = weights[i*2+1];
if(value <= 0) continue;
bounds[i*2+1] = value * scale;
}
}
}
public void setCurrent(int index) {
if(!locked) throw new IllegalStateException("Cursor can only be moved when its in read mode!");
current = index;
}
public float getOffset(boolean width) {
if(!locked) throw new IllegalStateException("You can't read during write!");
float total = 0F;
for(int i = 0;i<current;i++) {
total += bounds[i*2+(width ? 0 : 1)];
}
return total;
}
public float get(boolean width) {
if(!locked) throw new IllegalStateException("You can't read during write!");
return bounds[current*2+(width ? 0 : 1)];
}
private void applyDefault(GuiComponent component) {
addBound(component.getWidth(), true);
addBound(component.getHeight(), true);
}
public void addWeight(float weight, boolean width) {
if(locked) return;
weights[current*2+(width ? 0 : 1)] = weight;
}
public void addBound(float bound, boolean width) {
if(locked) return;
bounds[current*2+(width ? 0 : 1)] = bound;
}
}

View File

@ -9,10 +9,21 @@ public class ConstraintContainer {
private ConstraintContainer() {}
public void apply(GuiComponent owner, GuiComponent parent) {
public void apply(GuiComponent owner, GuiComponent parent, ConstrainedContext context) {
for(int i = 0;i<4;i++) {
if(constraints[i] == null) continue;
constraints[i].apply(owner, parent, Target.by(i));
constraints[i].apply(owner, parent, Target.by(i), context);
}
}
public void fetch(GuiComponent owner, GuiComponent parent, ConstrainedContext context) {
for(int i = 2;i<CONSTRAINT_LENGTH;i++) {
Target target = Target.by(i);
if(constraints[i] == null) {
context.addBound(target.get(owner), target.isXAxis());
continue;
}
constraints[i].fetch(owner, parent, target, context);
}
}

View File

@ -14,9 +14,13 @@ public class Constraints {
public static Pixels inverted(float value) { return new Pixels(value, true); }
@Override
public void apply(IGuiBox owner, IGuiBox parent, Target target) {
public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
target.set(owner, inverted ? target.asArea().get(parent) - value : value);
}
@Override
public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
context.addBound(inverted ? target.asArea().get(parent) - value : value, target.isXAxis());
}
}
public static record Parent(float padding, boolean inv) implements ISimpleConstraint {
@ -27,10 +31,16 @@ public class Constraints {
public static Parent inverted(float padding) { return new Parent(padding, true); }
@Override
public void apply(IGuiBox owner, IGuiBox parent, Target target) {
public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
if(inv) target.set(owner, target.isPosition() ? target.asArea().get(parent) - padding : padding * 2);
else target.set(owner, target.isPosition() ? padding : target.get(parent) - padding * 2);
}
@Override
public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
if(inv) context.addBound(target.isPosition() ? target.asArea().get(parent) - padding : padding * 2, target.isXAxis());
else context.addBound(target.isPosition() ? padding : target.get(parent) - padding * 2, target.isXAxis());
}
}
public static record Children(float padding, boolean includeChildOffsets) implements ISimpleConstraint {
@ -41,17 +51,27 @@ public class Constraints {
public static Children withPos(float padding) { return new Children(padding, true); }
@Override
public void apply(IGuiBox owner, IGuiBox parent, Target target) {
public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
if(target.isPosition()) return;
float value = 0F;
List<IGuiBox> children = owner.children();
for(int i = 0,m=children.size();i<m;i++) {
IGuiBox child = children.get(i);
//TODO Fix this. Shouldn't this be width + offset instead of offset + width?
value = Math.max(value, child.getBaseX() + (includeChildOffsets() ? child.getBaseWidth() : 0));
value = Math.max(value, child.getBaseWidth() + (includeChildOffsets() ? child.getBaseX() : 0));
}
target.set(owner, value);
}
@Override
public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
float value = 0F;
List<IGuiBox> children = owner.children();
for(int i = 0,m=children.size();i<m;i++) {
IGuiBox child = children.get(i);
value = Math.max(value, child.getBaseWidth() + (includeChildOffsets() ? child.getBaseX() : 0));
}
context.addBound(value, target.isXAxis());
}
}
public static record Relative(float value, float padding) implements ISimpleConstraint {
@ -60,9 +80,30 @@ public class Constraints {
public static Relative of(float value, float padding) { return new Relative(value, padding); }
@Override
public void apply(IGuiBox owner, IGuiBox parent, Target target) {
public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
target.set(owner, value * (target.asArea().get(parent) - padding));
}
@Override
public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
context.addBound(value * (target.asArea().get(parent) - padding), target.isXAxis());
}
}
public static record Weighted(float weight) implements ISimpleConstraint {
public static Weighted of(float weight) { return new Weighted(weight); }
@Override
public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
target.set(owner, target.get(context));
}
@Override
public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
if(target.isPosition()) return;
context.addWeight(weight, target.isXAxis());
}
}
public static record Center() implements ISimpleConstraint {
@ -71,18 +112,29 @@ public class Constraints {
public static Center of() { return INSTANCE; }
@Override
public void apply(IGuiBox owner, IGuiBox parent, Target target) {
public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
Target bounds = target.asArea();
target.set(owner, (bounds.get(parent) * 0.5F) - (bounds.get(owner) * 0.5F));
}
@Override
public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
context.addBound(target.get(owner), target.isXAxis());
}
}
public static record Conditional(BooleanSupplier supplier, IConstraint onTrue, IConstraint onFalse) implements IConstraint {
public static Conditional of(BooleanSupplier supplier, IConstraint onTrue, IConstraint onFalse) { return new Conditional(supplier, onTrue, onFalse); }
@Override
public void apply(GuiComponent owner, GuiComponent parent, Target target) {
(supplier.getAsBoolean() ? onTrue : onFalse).apply(owner, parent, target);
public void apply(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context) {
(supplier.getAsBoolean() ? onTrue : onFalse).apply(owner, parent, target, context);
}
@Override
public void fetch(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context) {
(supplier.getAsBoolean() ? onTrue : onFalse).fetch(owner, parent, target, context);
}
}
}

View File

@ -9,10 +9,12 @@ import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Condi
import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Parent;
import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Pixels;
import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Relative;
import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Weighted;
import speiger.src.coreengine.rendering.guiOld.helper.box.IGuiBox;
public interface IConstraint {
public void apply(GuiComponent owner, GuiComponent child, Target target);
public void apply(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context);
public void fetch(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context);
public static IConstraint pixels(float value) { return Pixels.of(value); }
public static IConstraint pixelsInv(float value) { return Pixels.inverted(value); }
@ -30,13 +32,19 @@ public interface IConstraint {
public static IConstraint relative(float value) { return Relative.of(value); }
public static IConstraint relative(float value, float padding) { return Relative.of(value, padding); }
public static IConstraint weighted(float weight) { return Weighted.of(weight); }
public static IConstraint center() { return Center.of(); }
public static IConstraint conditional(BooleanSupplier supplier, IConstraint onTrue, IConstraint onFalse) { return Conditional.of(supplier, onTrue, onFalse); }
public static interface ISimpleConstraint extends IConstraint {
@Override
default void apply(GuiComponent owner, GuiComponent parent, Target target) { apply(owner.getBox(), parent == null ? owner.screen().getBox() : parent.getBox(), target); }
public void apply(IGuiBox owner, IGuiBox parent, Target target);
default void apply(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context) { apply(owner.getBox(), parent == null ? owner.screen().getBox() : parent.getBox(), target, context); }
public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context);
@Override
default void fetch(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context) { fetch(owner.getBox(), parent == null ? owner.screen().getBox() : parent.getBox(), target, context); }
public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context);
}
public static enum Target {
@ -51,6 +59,7 @@ public interface IConstraint {
public static Target pos(boolean x) { return x ? X : Y; }
public static Target bounds(boolean x) { return x ? WIDTH : HEIGHT; }
public boolean isPosition() { return this == X || this == Y; }
public boolean isXAxis() { return this == X || this == WIDTH; }
public Target asArea() {
return switch(this) {
case X -> WIDTH;
@ -61,6 +70,7 @@ public interface IConstraint {
public float get(GuiComponent comp) { return get(comp.getBox()); }
public void set(GuiComponent comp, float value) { set(comp.getBox(), value); }
public float get(ConstrainedContext context) { return (isPosition() ? context.getOffset(isXAxis()) : context.get(isXAxis())); }
public float get(IGuiBox box) {
return switch(this) {