diff --git a/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/base/GUI.java b/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/base/GUI.java index 62f22e8e..4e5cdf79 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/base/GUI.java +++ b/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/base/GUI.java @@ -1,6 +1,7 @@ package com.bartlomiejpluta.base.api.game.gui.base; import com.bartlomiejpluta.base.api.game.gui.component.Component; +import com.bartlomiejpluta.base.api.game.gui.window.Window; import com.bartlomiejpluta.base.api.game.input.KeyEventHandler; import com.bartlomiejpluta.base.api.internal.gc.Disposable; import com.bartlomiejpluta.base.api.internal.render.Renderable; @@ -23,6 +24,8 @@ public interface GUI extends Renderable, Disposable, KeyEventHandler { Component inflateComponent(String widgetUid); + Window inflateWindow(String widgetUid); + Widget getRoot(); void setRoot(Widget root); diff --git a/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/window/BaseWindow.java b/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/window/BaseWindow.java index 65162ada..e39ecdaf 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/window/BaseWindow.java +++ b/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/window/BaseWindow.java @@ -1,5 +1,6 @@ package com.bartlomiejpluta.base.api.game.gui.window; +import com.bartlomiejpluta.base.api.game.context.Context; import com.bartlomiejpluta.base.api.game.gui.base.BaseWidget; import com.bartlomiejpluta.base.api.game.gui.base.GUI; import com.bartlomiejpluta.base.api.game.gui.component.Component; @@ -9,9 +10,29 @@ import com.bartlomiejpluta.base.api.game.screen.Screen; import static java.util.Objects.requireNonNull; public abstract class BaseWindow extends BaseWidget implements Window { + protected Context context; + protected GUI gui; protected Component content; protected WindowPosition windowPosition; + protected BaseWindow(Context context, GUI gui) { + this.context = context; + this.gui = gui; + } + + @Override + public void setContent(Component component) { + if (this.content != null) { + this.content.setParent(null); + } + + this.content = component; + + if (component != null) { + component.setParent(this); + } + } + @Override public WindowPosition getWindowPosition() { return windowPosition; @@ -38,16 +59,6 @@ public abstract class BaseWindow extends BaseWidget implements Window { content.draw(screen, gui); } - @Override - public void onOpen(WindowManager manager) { - // do nothing - } - - @Override - public void onClose(WindowManager manager) { - // do nothing - } - @Override public void handleKeyEvent(KeyEvent event) { content.handleKeyEvent(event); diff --git a/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/window/Window.java b/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/window/Window.java index ec12d6be..99811cd2 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/window/Window.java +++ b/api/src/main/java/com/bartlomiejpluta/base/api/game/gui/window/Window.java @@ -1,13 +1,20 @@ package com.bartlomiejpluta.base.api.game.gui.window; import com.bartlomiejpluta.base.api.game.gui.base.Widget; +import com.bartlomiejpluta.base.api.game.gui.component.Component; public interface Window extends Widget { + void setContent(Component component); + WindowPosition getWindowPosition(); void setWindowPosition(WindowPosition windowPosition); - void onOpen(WindowManager manager); + default void onOpen(WindowManager manager) { + // do nothing + } - void onClose(WindowManager manager); + default void onClose(WindowManager manager) { + // do nothing + } } diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/context/manager/DefaultContextManager.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/context/manager/DefaultContextManager.java index a6c43287..21840553 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/context/manager/DefaultContextManager.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/context/manager/DefaultContextManager.java @@ -6,7 +6,7 @@ import com.bartlomiejpluta.base.engine.context.model.DefaultContext; import com.bartlomiejpluta.base.engine.core.engine.GameEngine; import com.bartlomiejpluta.base.engine.gui.manager.FontManager; import com.bartlomiejpluta.base.engine.gui.manager.WidgetDefinitionManager; -import com.bartlomiejpluta.base.engine.gui.xml.inflater.ComponentInflater; +import com.bartlomiejpluta.base.engine.gui.xml.inflater.Inflater; import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration; import com.bartlomiejpluta.base.engine.project.serial.ProjectDeserializer; import com.bartlomiejpluta.base.engine.util.reflection.ClassLoader; @@ -35,7 +35,7 @@ public class DefaultContextManager implements ContextManager { private final FontManager fontManager; private final EntityManager entityManager; private final ClassLoader classLoader; - private final ComponentInflater inflater; + private final Inflater inflater; private final WidgetDefinitionManager widgetDefinitionManager; @SneakyThrows diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/context/model/DefaultContext.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/context/model/DefaultContext.java index 5bddf781..3b5bdf15 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/context/model/DefaultContext.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/context/model/DefaultContext.java @@ -14,7 +14,7 @@ import com.bartlomiejpluta.base.engine.core.engine.GameEngine; import com.bartlomiejpluta.base.engine.gui.manager.FontManager; import com.bartlomiejpluta.base.engine.gui.manager.WidgetDefinitionManager; import com.bartlomiejpluta.base.engine.gui.render.NanoVGGUI; -import com.bartlomiejpluta.base.engine.gui.xml.inflater.ComponentInflater; +import com.bartlomiejpluta.base.engine.gui.xml.inflater.Inflater; import com.bartlomiejpluta.base.engine.world.entity.manager.EntityManager; import com.bartlomiejpluta.base.engine.world.image.manager.ImageManager; import com.bartlomiejpluta.base.engine.world.map.manager.MapManager; @@ -48,7 +48,7 @@ public class DefaultContext implements Context { private final FontManager fontManager; @NonNull - private final ComponentInflater inflater; + private final Inflater inflater; @NonNull private final WidgetDefinitionManager widgetDefinitionManager; diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/gui/render/NanoVGGUI.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/gui/render/NanoVGGUI.java index f3cb85d1..c3c00c2e 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/gui/render/NanoVGGUI.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/gui/render/NanoVGGUI.java @@ -4,6 +4,7 @@ import com.bartlomiejpluta.base.api.game.camera.Camera; import com.bartlomiejpluta.base.api.game.context.Context; import com.bartlomiejpluta.base.api.game.gui.base.*; import com.bartlomiejpluta.base.api.game.gui.component.Component; +import com.bartlomiejpluta.base.api.game.gui.window.Window; import com.bartlomiejpluta.base.api.game.input.KeyEvent; import com.bartlomiejpluta.base.api.game.screen.Screen; import com.bartlomiejpluta.base.api.internal.render.ShaderManager; @@ -11,7 +12,7 @@ import com.bartlomiejpluta.base.engine.error.AppException; import com.bartlomiejpluta.base.engine.gui.manager.FontManager; import com.bartlomiejpluta.base.engine.gui.manager.WidgetDefinitionManager; import com.bartlomiejpluta.base.engine.gui.widget.ScreenWidget; -import com.bartlomiejpluta.base.engine.gui.xml.inflater.ComponentInflater; +import com.bartlomiejpluta.base.engine.gui.xml.inflater.Inflater; import com.bartlomiejpluta.base.engine.world.image.manager.ImageManager; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -35,7 +36,7 @@ public class NanoVGGUI implements GUI { private final Context context; private final FontManager fontManager; private final ImageManager imageManager; - private final ComponentInflater inflater; + private final Inflater inflater; private final WidgetDefinitionManager widgetDefinitionManager; private long nvg; @@ -69,7 +70,14 @@ public class NanoVGGUI implements GUI { public Component inflateComponent(String widgetUid) { log.info("Inflating component by widget definition with UID: [{}]", widgetUid); var is = widgetDefinitionManager.loadObject(widgetUid); - return inflater.inflate(is, context, this); + return inflater.inflateComponent(is, context, this); + } + + @Override + public Window inflateWindow(String widgetUid) { + log.info("Inflating window by widget definition with UID: [{}]", widgetUid); + var is = widgetDefinitionManager.loadObject(widgetUid); + return inflater.inflateWindow(is, context, this); } @Override diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/gui/xml/inflater/ComponentInflater.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/gui/xml/inflater/Inflater.java similarity index 54% rename from engine/src/main/java/com/bartlomiejpluta/base/engine/gui/xml/inflater/ComponentInflater.java rename to engine/src/main/java/com/bartlomiejpluta/base/engine/gui/xml/inflater/Inflater.java index ec350cea..6c403ddd 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/gui/xml/inflater/ComponentInflater.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/gui/xml/inflater/Inflater.java @@ -4,6 +4,9 @@ import com.bartlomiejpluta.base.api.game.context.Context; import com.bartlomiejpluta.base.api.game.gui.base.GUI; import com.bartlomiejpluta.base.api.game.gui.base.SizeMode; import com.bartlomiejpluta.base.api.game.gui.component.Component; +import com.bartlomiejpluta.base.api.game.gui.window.Window; +import com.bartlomiejpluta.base.api.game.gui.window.WindowPosition; +import com.bartlomiejpluta.base.engine.error.AppException; import com.bartlomiejpluta.base.engine.util.reflection.ClassLoader; import lombok.SneakyThrows; import org.codehaus.janino.ExpressionEvaluator; @@ -18,14 +21,17 @@ import java.io.InputStream; import java.util.stream.Stream; @org.springframework.stereotype.Component -public class ComponentInflater { - private static final String[] IMPORTS = Stream.of(GUI.class, SizeMode.class).map(Class::getCanonicalName).toArray(String[]::new); +public class Inflater { + private static final String[] IMPORTS = Stream.of(GUI.class, SizeMode.class, WindowPosition.class) + .map(Class::getCanonicalName) + .toArray(String[]::new); + private final DocumentBuilder builder; private final ClassLoader loader; @Autowired @SneakyThrows - public ComponentInflater(ClassLoader loader) { + public Inflater(ClassLoader loader) { this.loader = loader; var factory = DocumentBuilderFactory.newInstance(); @@ -34,21 +40,98 @@ public class ComponentInflater { } @SneakyThrows - public Component inflate(String xml, Context context, GUI gui) { + public Component inflateComponent(String xml, Context context, GUI gui) { var document = builder.parse(xml); - return parseNode(document.getDocumentElement(), context, gui); + return inflateComponent(document.getDocumentElement(), context, gui); } @SneakyThrows - public Component inflate(File file, Context context, GUI gui) { + public Component inflateComponent(File file, Context context, GUI gui) { var document = builder.parse(file); - return parseNode(document.getDocumentElement(), context, gui); + return inflateComponent(document.getDocumentElement(), context, gui); } @SneakyThrows - public Component inflate(InputStream is, Context context, GUI gui) { + public Component inflateComponent(InputStream is, Context context, GUI gui) { var document = builder.parse(is); - return parseNode(document.getDocumentElement(), context, gui); + return inflateComponent(document.getDocumentElement(), context, gui); + } + + @SneakyThrows + public Window inflateWindow(String xml, Context context, GUI gui) { + var document = builder.parse(xml); + return inflateWindow(document.getDocumentElement(), context, gui); + } + + @SneakyThrows + public Window inflateWindow(File file, Context context, GUI gui) { + var document = builder.parse(file); + return inflateWindow(document.getDocumentElement(), context, gui); + } + + @SneakyThrows + public Window inflateWindow(InputStream is, Context context, GUI gui) { + var document = builder.parse(is); + return inflateWindow(document.getDocumentElement(), context, gui); + } + + @SneakyThrows + private Window inflateWindow(Node root, Context context, GUI gui) { + var uri = root.getNamespaceURI(); + var name = root.getLocalName(); + + if (uri != null) { + name = uri + "." + name; + } + + var canonicalName = name.replaceAll("\\*", "").replaceAll("\\.+", "."); + + var windowClass = loader.loadClass(canonicalName); + + var window = (Window) windowClass.getConstructor(Context.class, GUI.class).newInstance(context, gui); + var attributes = root.getAttributes(); + + // Set attributes via setter methods + for (int i = 0; i < attributes.getLength(); ++i) { + var attribute = attributes.item(i); + + // Ignore namespaces definitions + if ("xmlns".equals(attribute.getPrefix())) { + continue; + } + + var setterName = createSetterMethodName(attribute.getLocalName()); + var value = parseValue(attribute.getNodeValue()); + var setter = windowClass.getMethod(setterName, value.getClass()); + setter.invoke(window, value); + } + + // Parse window content + var children = root.getChildNodes(); + + boolean hasContent = false; + + for (int i = 0; i < children.getLength(); ++i) { + var child = children.item(i); + + if (child.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + if (hasContent) { + throw new AppException("Window can have at most 1 child"); + } + + var content = parseNode(child, context, gui); + window.setContent(content); + hasContent = true; + } + + return window; + } + + private Component inflateComponent(Node root, Context context, GUI gui) { + return parseNode(root, context, gui); } @SneakyThrows