Skip to content

Commit

Permalink
Add array support to XML
Browse files Browse the repository at this point in the history
  • Loading branch information
nea89o committed Sep 18, 2023
1 parent 9983020 commit 6f67b64
Show file tree
Hide file tree
Showing 9 changed files with 330 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package io.github.moulberry.moulconfig.gui.elements;

import io.github.moulberry.moulconfig.gui.GuiElementNew;
import io.github.moulberry.moulconfig.gui.GuiImmediateContext;
import io.github.moulberry.moulconfig.observer.ObservableList;
import lombok.Getter;
import net.minecraft.client.renderer.GlStateManager;

import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;

@Getter
public class GuiElementArray<T> extends GuiElementNew {
public final ObservableList<T> list;

public final Function<? super T, ? extends GuiElementNew> render;
public List<GuiElementNew> guiElements;
public IdentityHashMap<T, GuiElementNew> cache = new IdentityHashMap<>();

private int width, height;

public GuiElementArray(ObservableList<T> list, Function<? super T, ? extends GuiElementNew> render) {
this.list = list;
this.render = render;
list.setObserver(this::reinitializeChildren);
reinitializeChildren();
}

public void reinitializeChildren() {
width = 0;
height = 0;
guiElements = new ArrayList<>();
for (T t : list) {
GuiElementNew apply = cache.computeIfAbsent(t, render);
width = Math.max(apply.getWidth(), width);
height += apply.getHeight();
guiElements.add(apply);
}
}

@Override
public <T> T foldChildren(T initial, BiFunction<GuiElementNew, T, T> visitor) {
for (GuiElementNew guiElement : guiElements) {
initial = visitor.apply(guiElement, initial);
}
return initial;
}

public void foldWithContext(GuiImmediateContext context, BiConsumer<GuiElementNew, GuiImmediateContext> visitor) {
foldChildren(0, (child, position) -> {
visitor.accept(child, context.translated(0, position, child.getWidth(), child.getHeight()));
return child.getHeight() + position;
});
}

@Override
public int getWidth() {
return width;
}

@Override
public int getHeight() {
return height;
}

@Override
public void render(GuiImmediateContext context) {
GlStateManager.pushMatrix();
foldWithContext(context, (child, childContext) -> {
child.render(childContext);
GlStateManager.translate(0, child.getHeight(), 0);
});
GlStateManager.popMatrix();
}

@Override
public void mouseEvent(GuiImmediateContext context) {
foldWithContext(context, GuiElementNew::mouseEvent);
}

@Override
public void keyboardEvent(GuiImmediateContext context) {
foldWithContext(context, GuiElementNew::keyboardEvent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.github.moulberry.moulconfig.internal;

public class TypeUtils {
public static boolean areTypesEquals(Class<?> a, Class<?> b) {
return normalizeNative(a) == normalizeNative(b);
}

public static Class<?> normalizeNative(Class<?> clazz) {
if (clazz == int.class) return Integer.class;
if (clazz == float.class) return Float.class;
if (clazz == double.class) return Double.class;
if (clazz == boolean.class) return Boolean.class;
if (clazz == long.class) return Long.class;
if (clazz == short.class) return Short.class;
if (clazz == char.class) return Character.class;
return clazz;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package io.github.moulberry.moulconfig.observer;

import lombok.Data;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

@Data
public class ObservableList<T> implements List<T> {
final List<T> delegate;
Runnable observer;

@Override
public int size() {
return delegate.size();
}

@Override
public boolean isEmpty() {
return delegate.isEmpty();
}

@Override
public boolean contains(Object o) {
return delegate.contains(o);
}

@NotNull
@Override
public Iterator<T> iterator() {
return delegate.iterator();
}

@NotNull
@Override
public Object[] toArray() {
return delegate.toArray();
}

@NotNull
@Override
public <T1> T1[] toArray(@NotNull T1[] a) {
return delegate.toArray(a);
}

@Override
public boolean add(T t) {
boolean a = delegate.add(t);
if (a)
update();
return a;
}

private void update() {
if (observer != null)
observer.run();
}

@Override
public boolean remove(Object o) {
boolean a = delegate.remove(o);
if (a)
update();
return a;
}

@Override
public boolean containsAll(@NotNull Collection<?> c) {
return delegate.containsAll(c);
}

@Override
public boolean addAll(@NotNull Collection<? extends T> c) {
boolean a = delegate.addAll(c);
if (a) update();
return a;
}

@Override
public boolean addAll(int index, @NotNull Collection<? extends T> c) {
boolean b = delegate.addAll(index, c);
if (b) update();
return b;
}

@Override
public boolean removeAll(@NotNull Collection<?> c) {
boolean b = delegate.removeAll(c);
if (b) update();
return b;
}

@Override
public boolean retainAll(@NotNull Collection<?> c) {
boolean b = delegate.retainAll(c);
if (b) update();
return b;
}

@Override
public void clear() {
delegate.clear();
update();
}

@Override
public T get(int index) {
return delegate.get(index);
}

@Override
public T set(int index, T element) {
T set = delegate.set(index, element);
update();
return set;
}

@Override
public void add(int index, T element) {
delegate.add(index, element);
update();
}

@Override
public T remove(int index) {
T remove = delegate.remove(index);
update();
return remove;
}

@Override
public int indexOf(Object o) {
return delegate.indexOf(o);
}

@Override
public int lastIndexOf(Object o) {
return delegate.lastIndexOf(o);
}

@NotNull
@Override
public ListIterator<T> listIterator() {
return listIterator(0);
}

@NotNull
@Override
public ListIterator<T> listIterator(int index) {
throw new UnsupportedOperationException();
}

@NotNull
@Override
public List<T> subList(int fromIndex, int toIndex) {
return delegate.subList(fromIndex, toIndex);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@

public interface Observer<T> {
void observeChange(T oldValue, T newValue);

default boolean isValid() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.github.moulberry.moulconfig.gui.*;
import io.github.moulberry.moulconfig.gui.elements.*;
import io.github.moulberry.moulconfig.internal.RenderUtils;
import io.github.moulberry.moulconfig.observer.ObservableList;
import io.github.moulberry.moulconfig.observer.Property;
import io.github.moulberry.moulconfig.processor.BuiltinMoulConfigGuis;
import io.github.moulberry.moulconfig.processor.ConfigProcessorDriver;
Expand All @@ -50,6 +51,8 @@
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.lwjgl.input.Mouse;

import java.util.Arrays;

@Mod(modid = "moulconfig", name = "MoulConfig")
public class MoulConfigTest {

Expand Down Expand Up @@ -149,5 +152,7 @@ public void processCommand(ICommandSender sender, String[] args) {
public static class ObjectBound {
@Bind
public boolean value;
@Bind
public ObservableList<String> data = new ObservableList<>(Arrays.asList("Hehhehehe", "Hohohohoho", "Hihihihi"));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.moulberry.moulconfig.xml;

import io.github.moulberry.moulconfig.internal.TypeUtils;
import io.github.moulberry.moulconfig.observer.GetSetter;
import lombok.Data;
import lombok.SneakyThrows;
Expand All @@ -9,15 +10,34 @@
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Data
public class XMLBoundProperties {
final Map<String, Field> namedProperties = new HashMap<>();
private static MethodHandles.Lookup lookup = MethodHandles.lookup();

public <T> GetSetter<T> getBoundProperty(String name, Class<T> clazz, Object object) {
if (name.equals("this")) {
if (!TypeUtils.areTypesEquals(clazz, object.getClass())) {
throw new IllegalArgumentException("Bind target " + name + " is of the wrong type " + object.getClass() + " instead of " + clazz);
}
return new GetSetter<T>() {
@Override
public T get() {
return (T) object;
}

@Override
public void set(T newValue) {
throw new UnsupportedOperationException();
}
};
}
var field = namedProperties.get(name);
if (field == null) throw new NullPointerException("Could not find bind target for " + name + " in " + clazz);
if (!TypeUtils.areTypesEquals(field.getType(), clazz))
throw new IllegalArgumentException("Bind target " + name + " is of the wrong type " + field.getType() + " instead of " + clazz);
field.setAccessible(true);
try {
var getter = lookup.unreflectGetter(field).bindTo(object);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public static XMLUniverse getDefaultUniverse() {
var xmlUniverse = new XMLUniverse();
xmlUniverse.registerLoader(new SwitchLoader());
xmlUniverse.registerLoader(new GuiLoader());
xmlUniverse.registerLoader(new ArrayLoader());
xmlUniverse.registerLoader(new ColumnLoader());
xmlUniverse.registerLoader(new RowLoader());
xmlUniverse.registerLoader(new TextLoader());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.moulberry.moulconfig.xml.loaders;

import io.github.moulberry.moulconfig.gui.GuiElementNew;
import io.github.moulberry.moulconfig.gui.elements.GuiElementArray;
import io.github.moulberry.moulconfig.observer.GetSetter;
import io.github.moulberry.moulconfig.observer.ObservableList;
import io.github.moulberry.moulconfig.xml.XMLContext;
import io.github.moulberry.moulconfig.xml.XMLGuiLoader;
import io.github.moulberry.moulconfig.xml.XMLUniverse;
import org.w3c.dom.Element;

import javax.xml.namespace.QName;

public class ArrayLoader implements XMLGuiLoader<GuiElementNew> {
@Override
public GuiElementNew createInstance(XMLContext<?> context, Element element) {
GetSetter<ObservableList> list = context.getPropertyFromAttribute(element, new QName("data"), ObservableList.class);
return new GuiElementArray<Object>(
list.get(),
object -> context.getChildFragment(element, object)
);
}

@Override
public QName getName() {
return XMLUniverse.qName("Array");
}
}
3 changes: 3 additions & 0 deletions src/main/resources/assets/moulconfig/test.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@
animationSpeed="100"
/>
</Row>
<Array data="@data">
<Text text="@this" width="80"/>
</Array>
</Column>
</Gui>

0 comments on commit 6f67b64

Please sign in to comment.