diff --git a/src/main/java/me/piitex/renjava/Launch.java b/src/main/java/me/piitex/renjava/Launch.java
index b79d79d4..99ff9e07 100644
--- a/src/main/java/me/piitex/renjava/Launch.java
+++ b/src/main/java/me/piitex/renjava/Launch.java
@@ -1,6 +1,7 @@
package me.piitex.renjava;
import javafx.application.Application;
+import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.io.*;
@@ -19,6 +20,8 @@
import me.piitex.renjava.configuration.RenJavaConfiguration;
import me.piitex.renjava.gui.GuiLoader;
import me.piitex.renjava.gui.Window;
+import me.piitex.renjava.gui.overlays.ButtonOverlay;
+import me.piitex.renjava.gui.prompts.Prompt;
import me.piitex.renjava.loggers.ApplicationLogger;
import me.piitex.renjava.loggers.RenLogger;
import org.reflections.Reflections;
@@ -93,7 +96,6 @@ private static void loadClass(Class> clazz, String[] args, InfoFile infoFile)
// Try to create info file if it didn't exist.
if (!infoFile.exists()) {
-
// Try to re-create
infoFile = new InfoFile(new File(renJava.getBaseDirectory(), "/renjava/build.info"), true);
if (!infoFile.exists()) {
@@ -117,7 +119,6 @@ private static void loadClass(Class> clazz, String[] args, InfoFile infoFile)
infoFile.write("main", clazz.getName());
infoFile.write("file", fileName);
-
} catch (URISyntaxException e) {
RenLogger.LOGGER.error("Could retrieve runtime information.", e);
}
@@ -179,6 +180,10 @@ public void start(Stage stage) {
/**
* This is just a default execute for testing purposes only.
+ * To make this execute work in your own environment, create a 'test' folder.
+ * The folder should be in the root directory of this project.
+ * Place gui default assets inside /~test/game/
+ * Run {@link Main#main(String[])}
*/
@Game(name = "Default Execute", author = "piitex", version = "0.0.0")
@Configuration(title = "{name}", width = 1920, height = 1080)
@@ -214,7 +219,29 @@ public void createStory() {
@Override
public void start() {
+ Prompt prompt = new Prompt("Are you sure you want to start a new game?");
+ prompt.setBlockMainWindow(false);
+
+ ButtonOverlay yes = new ButtonOverlay("yes", "Yes", Color.WHITE);
+ yes.setY(325);
+ prompt.addElement(yes);
+
+ yes.onClick(event -> {
+ // TODO: Play the first story of the game. (New game)
+ System.out.println("Starting new game...");
+ });
+
+ ButtonOverlay no = new ButtonOverlay("no", "No", Color.WHITE);
+ no.setY(325);
+ no.setX(840);
+
+ no.onClick(event -> {
+ prompt.closeWindow();
+ });
+
+ prompt.addElement(no);
+ prompt.render();
}
}
}
diff --git a/src/main/java/me/piitex/renjava/Main.java b/src/main/java/me/piitex/renjava/Main.java
index def9ee75..986fcbda 100644
--- a/src/main/java/me/piitex/renjava/Main.java
+++ b/src/main/java/me/piitex/renjava/Main.java
@@ -4,6 +4,7 @@ public class Main {
/**
* Initializes the javafx application.
+ * This is needed because the main execute cannot be executed while extending Application.
*
* @param args Application parameters.
*/
diff --git a/src/main/java/me/piitex/renjava/RenJava.java b/src/main/java/me/piitex/renjava/RenJava.java
index 4d958992..da5b0430 100644
--- a/src/main/java/me/piitex/renjava/RenJava.java
+++ b/src/main/java/me/piitex/renjava/RenJava.java
@@ -1,6 +1,7 @@
package me.piitex.renjava;
import javafx.application.HostServices;
+import javafx.application.Platform;
import javafx.scene.paint.Color;
import javafx.stage.StageStyle;
import me.piitex.renjava.addons.AddonLoader;
@@ -16,7 +17,9 @@
import me.piitex.renjava.configuration.SettingsProperties;
import me.piitex.renjava.events.EventHandler;
import me.piitex.renjava.events.defaults.*;
+import me.piitex.renjava.gui.GuiLoader;
import me.piitex.renjava.gui.Window;
+import me.piitex.renjava.gui.WindowBuilder;
import me.piitex.renjava.gui.containers.ScrollContainer;
import me.piitex.renjava.gui.layouts.VerticalLayout;
import me.piitex.renjava.gui.menus.MainMenu;
@@ -80,7 +83,6 @@ public abstract class RenJava {
protected String buildVersion;
-
// Error tracking
private static long lastErrorTimeStamp;
private static int spamTrack = 0;
@@ -99,6 +101,7 @@ protected void init() {
// Run after super
PLAYER = new Player();
TRACKS = new Tracks();
+ ADDONLOADER = new AddonLoader();
EVENTHANDLER = new EventHandler();
EVENTHANDLER.registerListener(new MenuClickEventListener());
@@ -109,25 +112,71 @@ protected void init() {
this.registerData(PLAYER);
this.registerData(TRACKS);
new RenLoader(this);
- ADDONLOADER = new AddonLoader();
}
+ public void reload(boolean resetGraphics) {
+ // Reloads the game. Do not call unless you know what you are doing.
+ PLAYER = null;
+ TRACKS = null;
+ EVENTHANDLER.getRegisteredListeners().clear();
+ registeredCharacters.clear();
+ registeredData.clear();
+ ADDONLOADER.disable();
+ ADDONLOADER = null;
+
+ gameWindow.clear();
+ gameWindow.render();
+
+ // Re-initialize.
+ init();
+
+ if (resetGraphics) {
+ gameWindow.getStage().setOnHiding(null);
+ gameWindow.getStage().hide();
+ new GuiLoader(gameWindow.getStage(), this, hostServices);
+ }
+ }
+
+ /**
+ * Used when displaying game information.
+ *
+ * @return The name of the project.
+ */
public String getName() {
return name;
}
+ /**
+ * Used when displaying game information.
+ *
+ * @return The author of the project.
+ */
public String getAuthor() {
return author;
}
+ /**
+ * Used when displaying game information.
+ *
+ * @return The version of the project.
+ */
public String getVersion() {
return version;
}
+ /**
+ * The engine will automatically create a unique id for every project. See {@link me.piitex.renjava.utils.MDUtils#getGameID(String)}
+ *
+ * The id is used as a unique game folder stored on the local system. Used for storing global data that can transfer between different save versions.
+ * @return The generated game id.
+ */
public Integer getID() {
return id;
}
+ /**
+ * @return The applications logger.
+ */
public Logger getLogger() {
return logger;
}
@@ -214,8 +263,6 @@ public void setBaseDir(File baseDir) {
/**
* Registers a character in the RenJava framework.
- *
- * The registerCharacter() method is used to register a character in the RenJava framework.
* Registered characters can be accessed and managed by other parts of the framework using their unique ID.
*
* @param character The character object to be registered.
@@ -233,8 +280,6 @@ public Collection getCharacters() {
/**
* Retrieves a character by its ID.
- *
- * The getCharacter() method is used to retrieve a character object based on its ID.
* Characters are registered using the registerCharacter() method and can be accessed using their unique ID.
*
* @param id The ID of the character to retrieve.
@@ -431,6 +476,14 @@ public static void openLink(String url) {
getInstance().getHost().showDocument(url);
}
+ /**
+ * Forcefully closes the application.
+ */
+ public static void shutdown() {
+ Platform.exit();
+ System.exit(0);
+ }
+
public static void writeStackTrace(Exception e) {
if (lastErrorTimeStamp > 0) {
spamTrack++;
@@ -467,7 +520,7 @@ public static void writeStackTrace(Exception e) {
}
if (errorWindow == null) {
- errorWindow = new Window("Error", StageStyle.DECORATED, CONFIGURATION.getGameIcon(), 920, 650, false);
+ errorWindow = new WindowBuilder("Error").setStageStyle(StageStyle.DECORATED).setIcon(CONFIGURATION.getGameIcon()).setDimensions(920, 650).setScale(false).build();
} else {
errorWindow.clearContainers();
}
@@ -493,11 +546,11 @@ public static void writeStackTrace(Exception e) {
texts.add(stackTrace);
TextFlowOverlay textFlowOverlay = new TextFlowOverlay(texts, 900, 600);
- rootLayout.addOverlay(textFlowOverlay);
+ rootLayout.addElement(textFlowOverlay);
- container.addLayout(rootLayout);
+ container.addElement(rootLayout);
- errorWindow.addContainers(container);
+ errorWindow.addContainer(container);
errorWindow.render();
}
diff --git a/src/main/java/me/piitex/renjava/RenLoader.java b/src/main/java/me/piitex/renjava/RenLoader.java
index fa96dc26..d38f35f0 100644
--- a/src/main/java/me/piitex/renjava/RenLoader.java
+++ b/src/main/java/me/piitex/renjava/RenLoader.java
@@ -32,8 +32,7 @@ public RenLoader(RenJava renJava) {
setupGame();
if (shutdown) {
// Shutdown application if startup fails.
- Platform.exit();
- System.exit(0);
+ RenJava.shutdown();
return;
}
@@ -244,8 +243,11 @@ public synchronized String getVersion() {
}
if (version == null) {
- // we could not compute the version so use a blank
- version = "";
+ // Please read and understand the RenJava license.
+ // Any modifications to RenJava that are sold for commercial use must be available.
+ // Any person can request source and all authors/developers must abide by that request.
+ // Sources must be disclosed this ensures safety and security of the application.
+ version = "Unknown (Request author for source)";
}
return version;
diff --git a/src/main/java/me/piitex/renjava/addons/AddonLoader.java b/src/main/java/me/piitex/renjava/addons/AddonLoader.java
index e92e9302..6ffbdb91 100644
--- a/src/main/java/me/piitex/renjava/addons/AddonLoader.java
+++ b/src/main/java/me/piitex/renjava/addons/AddonLoader.java
@@ -3,6 +3,7 @@
import me.piitex.renjava.RenJava;
import me.piitex.renjava.configuration.InfoFile;
import me.piitex.renjava.loggers.RenLogger;
+import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.jetbrains.annotations.Nullable;
@@ -11,7 +12,6 @@
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
-import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
@@ -24,6 +24,7 @@ public class AddonLoader {
private final List addons = new ArrayList<>();
private final Logger logger;
+
public AddonLoader() {
logger = RenLogger.LOGGER;
}
@@ -39,7 +40,7 @@ public void load() {
logger.info("No addons to load.");
return; // No need to load if there are no addons.
} else {
- logger.info("Loading " + size + " addon(s)...");
+ logger.info("Loading {} addon(s)...", size);
}
Map lateLoaders = new HashMap<>();
@@ -65,10 +66,18 @@ public void load() {
// Convert entry to file then load info file
try {
- File buildFile = new File(RenJava.getInstance().getBaseDirectory(), "addons/build.info");
- Files.copy(zipFile.getInputStream(entry), Path.of(buildFile.getPath()), StandardCopyOption.REPLACE_EXISTING);
+ File buildFile = File.createTempFile("build", ".info");
+ buildFile.deleteOnExit();
- InfoFile build = new InfoFile(buildFile, false);
+ InfoFile build;
+ try (FileOutputStream outputStream = new FileOutputStream(buildFile)) {
+ IOUtils.copy(zipFile.getInputStream(entry), outputStream);
+ build = new InfoFile(buildFile, false);
+ } catch (IOException e) {
+ RenLogger.LOGGER.error("Could not create temp-file for '{}'.", file.getName());
+ RenJava.writeStackTrace(e);
+ continue;
+ }
boolean invalidVersion = false;
@@ -107,9 +116,6 @@ public void load() {
nonDependants.add(file);
}
- // Delete after
- buildFile.delete();
-
extractResources(zipFile);
} catch (IOException e) {
@@ -130,12 +136,19 @@ public void load() {
}
});
+ // Java will scan the files randomly with no certain order.
+ // This will loop through the addons and attempt to load them.
+ // If the addon succeeds it will be added to passed and removed from validations.
+ // The loop will continue until validations is empty or the same addon fails twice.
Map validations = new HashMap<>(lateLoaders);
Collection passed = new HashSet<>();
AtomicReference lastValidated = new AtomicReference<>("");
while (!validations.isEmpty()) {
lateLoaders.forEach((file, string) -> {
+
+ // The same addon can only be looped twice in a row if it's the only addon left.
+ // If the addon failed twice it means there was a problem with that addon.
if (lastValidated.get().equalsIgnoreCase(file.getName())) {
logger.error("Could not initialize " + file.getName() + ": May be the result of a missing dependency.");
validations.remove(file);
@@ -145,9 +158,13 @@ public void load() {
return;
}
String dependency = string.trim();
+
+ // Dependencies are configured; dependencies=test1,test2
+ // If it contains a ',' it is likely to have multiple dependencies.
if (dependency.contains(",")) {
boolean canExecute = true;
Collection dep = new HashSet<>();
+ // Loop the dependencies by its comma. test1,test2
for (String depend : dependency.split(",")) {
Addon addon = addons.stream().filter(addon1 -> addon1.getName().equalsIgnoreCase(depend)).findAny().orElse(null);
if (addon == null) {
@@ -197,6 +214,11 @@ public void load() {
private void initAddon(File file, @Nullable Collection dependencies) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
try (JarFile jarFile = new JarFile(file)) {
Enumeration entries = jarFile.entries();
+
+ // Deprecation notice!
+ // new URL is deprecated and moved to new URI(str).toUrl();
+ // This method does not work with the string.
+ // So I'm keeping it in for now.
URL[] urls = {new URL("jar:file:" + file.getPath() + "!/")};
URLClassLoader cl = URLClassLoader.newInstance(urls);
while (entries.hasMoreElements()) {
@@ -207,7 +229,7 @@ private void initAddon(File file, @Nullable Collection dependencies) thro
// Authors should warn users about using pirated versions or getting addons from unknown sources.
// This can easily allow malicious code to be executed. I will not be adding any form on 'anti malware' checks. Don't download something you don't trust.
- // Also be aware of the licence renjava uses. Authors are required to provide source to code per the GPL 3.0 license.
+ // Also be aware of the licence renjava uses. Authors are required to provide source code per the GPL 3.0 license.
Class> clazz = cl.loadClass(clazzName);
if (Addon.class.isAssignableFrom(clazz)) {
Object object = clazz.getDeclaredConstructor().newInstance();
@@ -216,7 +238,6 @@ private void initAddon(File file, @Nullable Collection dependencies) thro
addon.getDependencies().addAll(dependencies);
}
addons.add(addon);
- //clazz.getMethod("onLoad").invoke(object, null);
boolean failed = false;
try {
addon.onLoad(); // Loads addon
diff --git a/src/main/java/me/piitex/renjava/api/exceptions/ImageNotFoundException.java b/src/main/java/me/piitex/renjava/api/exceptions/ImageNotFoundException.java
deleted file mode 100644
index 1119c1e0..00000000
--- a/src/main/java/me/piitex/renjava/api/exceptions/ImageNotFoundException.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package me.piitex.renjava.api.exceptions;
-
-import me.piitex.renjava.api.loaders.ImageLoader;
-
-public class ImageNotFoundException extends Exception {
-
- public ImageNotFoundException(ImageLoader loader) {
- super("Could not find image: " + loader.getFile().getAbsolutePath());
- }
-}
diff --git a/src/main/java/me/piitex/renjava/api/loaders/FontLoader.java b/src/main/java/me/piitex/renjava/api/loaders/FontLoader.java
index dc708cea..29db5334 100644
--- a/src/main/java/me/piitex/renjava/api/loaders/FontLoader.java
+++ b/src/main/java/me/piitex/renjava/api/loaders/FontLoader.java
@@ -41,7 +41,7 @@ public class FontLoader {
*
*
* {@code
- * FontLoader existingFont = new FontLoader("arial, 17"); // Font has a size of 17
+ * FontLoader existingFont = new FontLoader("arial", 17); // Font has a size of 17
* FontLoader newFont = new FontLoader(existingFont, 21); // Font has a size of 21.
* }
*
@@ -59,6 +59,13 @@ public FontLoader(FontLoader font, double size) {
/**
* Adjusts the size of a JavaFX font.
*
+ *
+ * {@code
+ * Font fxFont;
+ * FontLoader font = new Font(fxFont, 21);
+ * }
+ *
+ *
* @param font The JavaFX {@link Font}.
* @param size The new size of the font.
*/
@@ -139,7 +146,7 @@ public FontLoader(Font font, FontWeight weight, FontPosture posture, double size
/**
* Loads a system font or a file font. The file location must be inside '~game/fonts/~'.
- * The system must be installed onto the system for it too work.
+ * The system font must be installed onto the system for it too work. A file font does not need to be installed.
*
*
*
@@ -168,7 +175,7 @@ public FontLoader(String name, double size) {
/**
* Loads a system font or a file font. The file location must be inside '~game/fonts/~'.
- * The system must be installed onto the system for it too work.
+ * The system font must be installed onto the system for it too work. A file font does not need to be installed.
*
*
*
diff --git a/src/main/java/me/piitex/renjava/api/loaders/ImageLoader.java b/src/main/java/me/piitex/renjava/api/loaders/ImageLoader.java
index 0f8bc3f8..71e7a2b0 100644
--- a/src/main/java/me/piitex/renjava/api/loaders/ImageLoader.java
+++ b/src/main/java/me/piitex/renjava/api/loaders/ImageLoader.java
@@ -2,12 +2,10 @@
import javafx.scene.image.*;
import me.piitex.renjava.RenJava;
-import me.piitex.renjava.api.exceptions.ImageNotFoundException;
import me.piitex.renjava.loggers.RenLogger;
import me.piitex.renjava.utils.LimitedHashMap;
import org.apache.commons.io.IOUtils;
-import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
@@ -31,8 +29,10 @@
*/
public class ImageLoader {
private final File file;
+ private double width, height;
- private static final Map imageCache = new LimitedHashMap<>(50);
+ public static final Map imageCache = new LimitedHashMap<>(50);
+ private static final Map imageSizeCache = new LimitedHashMap<>(50);
/**
* Loads an image via a filename from the base directory or defaults to class-path.
@@ -46,7 +46,6 @@ public class ImageLoader {
*/
public ImageLoader(String name) {
File directory = new File(RenJava.getInstance().getBaseDirectory(), "game/images/");
-
File f = new File(directory, name);
// If the file does not exist check to see if its in the class path
@@ -118,34 +117,29 @@ public ImageLoader(String directory, String name) {
this.file = f;
}
- public Image build() throws ImageNotFoundException {
- if (imageCache.containsKey(file.getPath())) {
+ /**
+ * Builds the specified image file into an image object that can be rendered.
+ * The function will attempt to build into a {@link BufferedImage} first.
+ * The WebP-Image-IO library hooks into a buffered image allowing more support for image formats.
+ * If the file cannot be rendered into a BufferedImage it will try to be rendered as a base {@link Image}.
+ * The JavaFX image has limited formats and slower loading.
+ *
+ * @return A loaded {@link BufferedImage} or {@link Image}.
+ */
+ public Image build() {
+ if (imageCache.containsKey(file.getPath()) && (width + height == imageSizeCache.get(file.getPath()))) {
return imageCache.get(file.getPath());
}
+
try {
- BufferedImage bufferedImage = ImageIO.read(file);
- Image image = getImage(bufferedImage);
+ Image image = new Image(new FileInputStream(file), width, height, false, false);
imageCache.put(file.getPath(), image);
+ imageSizeCache.put(file.getPath(), width + height);
+
return image;
- } catch (FileNotFoundException ignored) {
- // Better logging
- ImageNotFoundException exception = new ImageNotFoundException(this);
- RenLogger.LOGGER.error(exception.getMessage(), exception);
- RenJava.writeStackTrace(exception);
- throw exception;
} catch (IOException e) {
- return buildRaw();
- }
- }
-
- public Image buildRaw() throws ImageNotFoundException {
- try {
- return new Image(new FileInputStream(file));
- } catch (FileNotFoundException e) {
- ImageNotFoundException exception = new ImageNotFoundException(this);
- RenLogger.LOGGER.error(exception.getMessage(), exception);
- RenJava.writeStackTrace(exception);
- throw exception;
+ e.printStackTrace();
+ return null;
}
}
@@ -153,8 +147,24 @@ public File getFile() {
return file;
}
+ public double getWidth() {
+ return width;
+ }
+
+ public void setWidth(double width) {
+ this.width = width;
+ }
+
+ public double getHeight() {
+ return height;
+ }
+
+ public void setHeight(double height) {
+ this.height = height;
+ }
+
- // Credit: https://stackoverflow.com/questions/30970005/bufferedimage-to-javafx-image
+ // Credit: https://stackoverflow.com/a/75703543
private Image getImage(BufferedImage img) {
//converting to a good type, read about types here: https://openjfx.io/javadoc/13/javafx.graphics/javafx/scene/image/PixelBuffer.html
BufferedImage newImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
diff --git a/src/main/java/me/piitex/renjava/api/player/Player.java b/src/main/java/me/piitex/renjava/api/player/Player.java
index 39d9def9..eb7a9b5c 100644
--- a/src/main/java/me/piitex/renjava/api/player/Player.java
+++ b/src/main/java/me/piitex/renjava/api/player/Player.java
@@ -240,6 +240,15 @@ public void updateScene(RenScene renScene, boolean rollback) {
setCurrentStory(renScene.getStory());
}
+ public boolean inGame() {
+ return (currentStageType == StageType.IMAGE_SCENE || currentStageType == StageType.ANIMATION_SCENE || currentStageType == StageType.CHOICE_SCENE ||
+ currentStageType == StageType.INPUT_SCENE || currentStageType == StageType.INTERACTABLE_SCENE);
+ }
+
+ public boolean inMenu() {
+ return !inGame();
+ }
+
public void resetSession() {
this.currentScene = null;
this.currentStory = null;
diff --git a/src/main/java/me/piitex/renjava/api/saves/Save.java b/src/main/java/me/piitex/renjava/api/saves/Save.java
index 826ee8cd..5c412088 100644
--- a/src/main/java/me/piitex/renjava/api/saves/Save.java
+++ b/src/main/java/me/piitex/renjava/api/saves/Save.java
@@ -7,11 +7,11 @@
import me.piitex.renjava.api.saves.file.SaveFileState;
import me.piitex.renjava.gui.Container;
import me.piitex.renjava.gui.Window;
+import me.piitex.renjava.gui.WindowBuilder;
import me.piitex.renjava.gui.overlays.ImageOverlay;
import me.piitex.renjava.loggers.RenLogger;
import me.piitex.renjava.api.scenes.RenScene;
import me.piitex.renjava.api.stories.Story;
-import me.piitex.renjava.tasks.Tasks;
import me.piitex.renjava.utils.FileCrypter;
import java.io.File;
@@ -124,7 +124,7 @@ public String retrieveCurrentData() {
}
- public ImageOverlay buildPreview(int page) {
+ public ImageOverlay buildPreview() {
if (!file.exists()) {
return null;
}
@@ -157,11 +157,10 @@ public ImageOverlay buildPreview(int page) {
// When the render function is called, the stage type will be set to scene type. This will cause issues as the player is technically in the save/load screen.
// To prevent the white flash when loading preview use diff window.
// On slower machines the window may pop-up for a few seconds but if that's the case your pc doesn't meet spec requirements to begin with.
- Window hiddenWindow = new Window("", StageStyle.DECORATED, null, 1920, 1080, false, false);
+ Window hiddenWindow = new WindowBuilder("").setStageStyle(StageStyle.DECORATED).setDimensions(1920, 1080).build();
-// hiddenWindow.clear(); // Required (This prevents white boxes from being rendered)
Container container = currentScene.build(true);
- hiddenWindow.addContainers(container);
+ hiddenWindow.addContainer(container);
hiddenWindow.build(true);
WritableImage snapshot = hiddenWindow.getRoot().getScene().snapshot(null);
diff --git a/src/main/java/me/piitex/renjava/api/saves/file/SectionKeyValue.java b/src/main/java/me/piitex/renjava/api/saves/file/SectionKeyValue.java
index 3bb2f29d..2cef52f8 100644
--- a/src/main/java/me/piitex/renjava/api/saves/file/SectionKeyValue.java
+++ b/src/main/java/me/piitex/renjava/api/saves/file/SectionKeyValue.java
@@ -38,6 +38,10 @@ public Collection getSubSections() {
return subSection;
}
+ public SectionKeyValue getSection(String section) {
+ return subSection.stream().filter(sectionKeyValue -> sectionKeyValue.getSection().equalsIgnoreCase(section)).findAny().orElse(null);
+ }
+
public Map> getArrayMap() {
return arrayMap;
}
diff --git a/src/main/java/me/piitex/renjava/api/scenes/RenScene.java b/src/main/java/me/piitex/renjava/api/scenes/RenScene.java
index 100323cc..3c9ebf34 100644
--- a/src/main/java/me/piitex/renjava/api/scenes/RenScene.java
+++ b/src/main/java/me/piitex/renjava/api/scenes/RenScene.java
@@ -1,6 +1,10 @@
package me.piitex.renjava.api.scenes;
import me.piitex.renjava.RenJava;
+import me.piitex.renjava.api.characters.Character;
+import me.piitex.renjava.api.loaders.FontLoader;
+import me.piitex.renjava.api.loaders.ImageLoader;
+import me.piitex.renjava.api.scenes.text.StringFormatter;
import me.piitex.renjava.api.scenes.transitions.types.FadingTransition;
import me.piitex.renjava.api.scenes.transitions.types.ImageFlashTransition;
import me.piitex.renjava.api.scenes.types.animation.VideoScene;
@@ -11,17 +15,23 @@
import me.piitex.renjava.api.scenes.types.input.InputScene;
import me.piitex.renjava.api.stories.Story;
+import me.piitex.renjava.configuration.RenJavaConfiguration;
import me.piitex.renjava.events.types.SceneRenderEvent;
import me.piitex.renjava.events.types.SceneStartEvent;
import me.piitex.renjava.gui.Container;
+import me.piitex.renjava.gui.Element;
import me.piitex.renjava.gui.StageType;
import me.piitex.renjava.gui.Window;
+import me.piitex.renjava.gui.containers.EmptyContainer;
import me.piitex.renjava.gui.overlays.ImageOverlay;
import me.piitex.renjava.gui.overlays.Overlay;
+import me.piitex.renjava.gui.overlays.TextFlowOverlay;
+import me.piitex.renjava.gui.overlays.TextOverlay;
import java.io.File;
import java.util.Collection;
import java.util.HashSet;
+import java.util.LinkedList;
/**
@@ -52,10 +62,12 @@ public abstract class RenScene {
private Transitions startTransition;
private Transitions endTransition;
- private final Collection additionalOverlays = new HashSet<>();
+ private final LinkedList elements = new LinkedList<>();
private final Collection styleSheets = new HashSet<>();
+ private final Window window = RenJava.getInstance().getGameWindow();
+
public RenScene(String id, ImageOverlay backgroundImage) {
this.id = id;
this.backgroundImage = backgroundImage;
@@ -123,6 +135,10 @@ public RenScene setEndTransition(Transitions transition) {
return this;
}
+ public Window getWindow() {
+ return window;
+ }
+
/**
* @return The beginning {@link Transitions}.
*/
@@ -168,19 +184,13 @@ public SceneBuildInterface getBuildInterface() {
return buildInterface;
}
- /**
- * Adds an overlay to the scene.
- * @param overlay The {@link Overlay} to be added.
- */
- public void addOverlay(Overlay overlay) {
- additionalOverlays.add(overlay);
+
+ public void addElement(Element element) {
+ elements.add(element);
}
- /**
- * @return All added overlays for the scene. This excludes the mandatory ones, like the background image and text-box.
- */
- public Collection getAdditionalOverlays() {
- return additionalOverlays;
+ public LinkedList getElements() {
+ return elements;
}
/**
@@ -249,6 +259,53 @@ public void render(Window window, boolean ui, boolean events) {
}
}
+ protected Container buildTextBox(Character character, String displayName, String dialogue, FontLoader font) {
+ RenJavaConfiguration configuration = RenJava.getConfiguration();
+
+ Container textboxMenu = new EmptyContainer(0, 0, configuration.getDialogueBoxWidth(), configuration.getDialogueBoxHeight());
+ String characterDisplay = null;
+ if (character != null) {
+ if (displayName != null) {
+ // Set character display
+ characterDisplay = displayName;
+ } else {
+ characterDisplay = character.getDisplayName();
+ }
+ }
+
+ if (dialogue != null && !dialogue.isEmpty()) {
+ ImageLoader textbox = new ImageLoader("gui/textbox.png");
+
+ ImageOverlay textBoxImage = new ImageOverlay(textbox, configuration.getDialogueBoxX() + configuration.getDialogueOffsetX(), configuration.getDialogueBoxY() + configuration.getDialogueOffsetY());
+ textboxMenu.addElement(textBoxImage);
+
+ LinkedList texts = StringFormatter.formatText(dialogue);
+ TextFlowOverlay textFlowOverlay;
+ if (texts.isEmpty()) {
+ TextOverlay text = new TextOverlay(dialogue);
+ text.setFont(RenJava.CONFIGURATION.getDialogueFont());
+ textFlowOverlay = new TextFlowOverlay(text, configuration.getDialogueBoxWidth(), configuration.getDialogueBoxHeight());
+ } else {
+ textFlowOverlay = new TextFlowOverlay(texts, configuration.getDialogueBoxWidth(), configuration.getDialogueBoxHeight());
+ }
+ textFlowOverlay.setX(configuration.getTextX() + configuration.getTextOffsetX());
+ textFlowOverlay.setY(configuration.getTextY() + configuration.getTextOffsetY());
+ textFlowOverlay.setTextFillColor(configuration.getDialogueColor());
+ textFlowOverlay.setFont(font);
+ textboxMenu.addElement(textFlowOverlay);
+
+ if (characterDisplay != null) {
+ TextOverlay characterText = new TextOverlay(characterDisplay, new FontLoader(configuration.getCharacterDisplayFont(), configuration.getCharacterTextSize()),
+ configuration.getCharacterTextX() + configuration.getCharacterTextOffsetX(),
+ configuration.getCharacterTextY() + configuration.getCharacterTextOffsetY());
+ characterText.setTextFill(character.getColor());
+ textboxMenu.addElement(characterText);
+ }
+ }
+
+ return textboxMenu;
+ }
+
/**
* @return The scene type.
*/
diff --git a/src/main/java/me/piitex/renjava/api/scenes/text/StringFormatter.java b/src/main/java/me/piitex/renjava/api/scenes/text/StringFormatter.java
index f6b4e049..773d4332 100644
--- a/src/main/java/me/piitex/renjava/api/scenes/text/StringFormatter.java
+++ b/src/main/java/me/piitex/renjava/api/scenes/text/StringFormatter.java
@@ -1,9 +1,5 @@
package me.piitex.renjava.api.scenes.text;
-import javafx.scene.text.Font;
-import javafx.scene.text.FontPosture;
-import javafx.scene.text.FontWeight;
-import javafx.scene.text.Text;
import me.piitex.renjava.RenJava;
import me.piitex.renjava.api.loaders.FontLoader;
@@ -12,144 +8,49 @@
import me.piitex.renjava.gui.overlays.TextOverlay;
import java.util.LinkedList;
+import java.util.regex.Pattern;
public class StringFormatter {
public static LinkedList formatText(String dialogue) {
- // If anyone wants to take a crack at this and optimize it be my guest.
-
- boolean italic = false;
- boolean bold = false;
- boolean strikeOut = false;
- int formatBeginChar = 0;
-
- LinkedList parts = new LinkedList<>();
- String beforeText = "";
- for (int i = 0; i < dialogue.length(); i++) {
- char c = dialogue.charAt(i);
- if (c == '{' && dialogue.charAt(i + 1) == 'i') {
- formatBeginChar = i + 1;
- italic = true;
- } else if (c == '{' && dialogue.charAt(i + 1) == 'b') {
- formatBeginChar = i + 1;
- bold = true;
- } else if (c == '{' && dialogue.charAt(i + 1) == 's') {
- formatBeginChar = i + 1;
- strikeOut = true;
- } else if (c == '{' && italic) {
- String italicText = dialogue.substring(formatBeginChar, i);
- parts.add(beforeText.replace(italicText, "").replace("/i}", "").replace("/b}", "").replace("/s}", ""));
- parts.add("iiii: " + italicText.replace("i}", ""));
- beforeText = "";
- italic = false;
- } else if (c == '{' && bold) {
- String boldText = dialogue.substring(formatBeginChar, i);
- parts.add(beforeText.replace(boldText, "").replace("/i}", "").replace("/b}", "").replace("/s}", ""));
- parts.add("bbbb: " + boldText.replace("b}", ""));
- beforeText = "";
- bold = false;
- } else if (c == '{' && strikeOut) {
- String strikeOutText = dialogue.substring(formatBeginChar, i);
- parts.add(beforeText.replace(strikeOutText, "").replace("/s}", "").replace("/b}", "").replace("/i}", ""));
- parts.add("ssss: " + strikeOutText.replace("s}", ""));
- beforeText = "";
- strikeOut = false;
- } else {
- // Process text that is not formatted.
- beforeText += c;
- }
- }
- beforeText = beforeText.replace("/i}", "").replace("/b}", "").replace("/s}", "");
- parts.add(beforeText);
-
- LinkedList texts = new LinkedList<>();
-
+ // {i} Italic text {/i}
+ // {b} Bold text {/b}
+ // {s} Strikeout text {/s}
RenJavaConfiguration configuration = RenJava.CONFIGURATION;
FontLoader currentFont = configuration.getDialogueFont();
- FontLoader italicFont = new FontLoader(currentFont.getFont(), FontWeight.NORMAL, FontPosture.ITALIC, currentFont.getSize());
- FontLoader boldFont = new FontLoader(currentFont.getFont(), FontWeight.BOLD, FontPosture.REGULAR, currentFont.getSize());
-
- for (String s : parts) {
- TextOverlay text1 = new TextOverlay(s);
- if (s.startsWith("bbbb: ")) {
- s = s.replace("bbbb: ", "");
- text1.setFont(boldFont);
- texts.add(text1);
- } else if (s.startsWith("iiii: ")) {
- s = s.replace("iiii: ", "");
- text1.setFont(italicFont);
- texts.add(text1);
- } else if (s.startsWith("ssss: ")) {
- s = s.replace("ssss: ", "");
- text1.setFont(currentFont);
- text1.setStrikeout(true);
- texts.add(text1);
- } else {
- text1.setFont(currentFont);
- texts.add(text1);
+ FontLoader italicFont = configuration.getItalicFont();
+ FontLoader boldFont = configuration.getBoldFont();
+
+ LinkedList toReturn = new LinkedList<>();
+ String[] split = dialogue.split("\\{");
+ for (String s : split) {
+ s = s.replaceFirst("/" + "(.*?)" + Pattern.quote("}"), "");
+ if (s.isEmpty()) continue;
+ if (s.matches("/" + "(.*?)" + Pattern.quote("}"))) {
+ continue;
}
- }
- return texts;
- }
-
- // Testing function. Should be removed later
- public static void main(String[] args) {
- String data = "{i}Heey...{/i} {s}I've been waiting for you.{/s} You were {b}SUPPOSED{/b} to be here by now.";
-
- boolean italic = false;
- boolean bold = false;
- boolean strikeOut = false;
-
- int formatBeginChar = 0;
-
- LinkedList parts = new LinkedList<>();
- String beforeText = "";
- for (int i = 0; i < data.length(); i++) {
- char c = data.charAt(i);
- if (c == '{' && data.charAt(i + 1) == 'i') {
- formatBeginChar = i + 1;
- italic = true;
- } else if (c == '{' && data.charAt(i + 1) == 'b') {
- formatBeginChar = i + 1;
- bold = true;
- } else if (c == '{' && data.charAt(i + 1) == 's') {
- formatBeginChar = i + 1;
- strikeOut = true;
- } else if (c == '{' && italic) {
- String italicText = data.substring(formatBeginChar, i);
- System.out.println("Italic Text: " + italicText);
- parts.add(beforeText.replace(italicText, "").replace("/i}", "").replace("/b}", "").replace("/s}", ""));
- parts.add(italicText.replace("i}", ""));
- beforeText = "";
- italic = false;
- } else if (c == '{' && bold) {
- String boldText = data.substring(formatBeginChar, i);
- System.out.println("Bold Text: " + boldText);
- System.out.println("Before Text: " + beforeText);
- parts.add(beforeText.replace(boldText, "").replace("/i}", "").replace("/b}", "").replace("/s}", ""));
- parts.add(boldText.replace("b}", ""));
- beforeText = "";
- bold = false;
- } else if (c == '{' && strikeOut) {
- String strikeOutText = data.substring(formatBeginChar, i);
- System.out.println("StrikeOut Text: " + strikeOutText);
- System.out.println("Before Text: " + beforeText);
- parts.add(beforeText.replace(strikeOutText, "").replace("/s}", "").replace("/b}", "").replace("/i}", ""));
- parts.add(strikeOutText.replace("s}", ""));
- beforeText = "";
- strikeOut = false;
+ if (s.startsWith("b}")) {
+ s = s.replace("b}", "");
+ TextOverlay textOverlay = new TextOverlay(s);
+ textOverlay.setFont(boldFont);
+ toReturn.add(textOverlay);
+ } else if (s.startsWith("i}")) {
+ s = s.replace("i}", "");
+ TextOverlay textOverlay = new TextOverlay(s);
+ textOverlay.setFont(italicFont);
+ toReturn.add(textOverlay);
+ } else if (s.startsWith("s}")) {
+ s = s.replace("b}", "");
+ TextOverlay textOverlay = new TextOverlay(s);
+ textOverlay.setFont(currentFont);
+ textOverlay.setStrikeout(true);
+ toReturn.add(textOverlay);
} else {
- // Process text that is not formatted.
- beforeText += c;
- System.out.println("Before Text: " + beforeText);
+ TextOverlay textOverlay = new TextOverlay(s);
+ textOverlay.setFont(currentFont);
+ toReturn.add(textOverlay);
}
}
- beforeText = beforeText.replaceAll("/i}", "").replace("/b}", "");
- parts.add(beforeText);
- String text = "";
- for (String s : parts) {
- text += s;
- }
- System.out.println("Text: " + text);
+ return toReturn;
}
}
\ No newline at end of file
diff --git a/src/main/java/me/piitex/renjava/api/scenes/transitions/types/FadingTransition.java b/src/main/java/me/piitex/renjava/api/scenes/transitions/types/FadingTransition.java
index c11d5dd7..941dc11f 100644
--- a/src/main/java/me/piitex/renjava/api/scenes/transitions/types/FadingTransition.java
+++ b/src/main/java/me/piitex/renjava/api/scenes/transitions/types/FadingTransition.java
@@ -1,7 +1,11 @@
package me.piitex.renjava.api.scenes.transitions.types;
import javafx.animation.FadeTransition;
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.animation.Timeline;
import javafx.scene.Node;
+import javafx.scene.media.MediaView;
import javafx.scene.paint.Color;
import javafx.util.Duration;
import me.piitex.renjava.RenJava;
@@ -82,6 +86,10 @@ public void play(Node root) {
});
playing = true;
RenJava.PLAYER.setCurrentTransition(this);
+ if (root instanceof MediaView mediaView) {
+ Timeline timeline = new Timeline(new KeyFrame(Duration.valueOf(getDuration() + "ms"), new KeyValue(mediaView.getMediaPlayer().volumeProperty(), toValue)));
+ timeline.play();
+ }
fadeTransition.play();
}
@@ -105,7 +113,7 @@ public void stop() {
}
try {
fadeTransition.stop();
- fadeTransition.getNode().setOpacity(1); // Resets opacity
+ fadeTransition.getNode().setOpacity(toValue);
handleEvents(scene);
} catch (Exception e) {
RenLogger.LOGGER.error("Error stopping transition!", e);
diff --git a/src/main/java/me/piitex/renjava/api/scenes/types/ImageScene.java b/src/main/java/me/piitex/renjava/api/scenes/types/ImageScene.java
index af72ee36..c7e98ed1 100644
--- a/src/main/java/me/piitex/renjava/api/scenes/types/ImageScene.java
+++ b/src/main/java/me/piitex/renjava/api/scenes/types/ImageScene.java
@@ -8,7 +8,6 @@
import me.piitex.renjava.configuration.RenJavaConfiguration;
import me.piitex.renjava.events.types.SceneBuildEvent;
import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.DisplayOrder;
import me.piitex.renjava.gui.StageType;
import me.piitex.renjava.api.loaders.FontLoader;
import me.piitex.renjava.api.loaders.ImageLoader;
@@ -36,13 +35,10 @@
*
* Example usage:
*
{@code
- * TODO
+ * ImageScene scene = new ImageScene("id", character, "Dialogue", new ImageOverlay("image.png");
* }
*
*
- *
- * Note: The ImageScene class is used to create image scenes in the RenJava framework.
- *
*/
public class ImageScene extends RenScene {
private Character character;
@@ -131,59 +127,14 @@ public void setDialogueFont(FontLoader font) {
public Container build(boolean ui) {
Container container = new EmptyContainer(configuration.getWidth(), configuration.getHeight());
if (backgroundImage != null) {
- backgroundImage.setOrder(DisplayOrder.LOW); // Bg should always be at low priority. They will be pushed to the back of the scene.
- container.addOverlays(backgroundImage);
+ container.addElement(backgroundImage);
} else {
BoxOverlay boxOverlay = new BoxOverlay(container.getWidth(), configuration.getHeight(), Color.BLACK);
- boxOverlay.setOrder(DisplayOrder.LOW);
- container.addOverlay(boxOverlay);
+ container.addElement(boxOverlay);
}
-
if (ui) {
- String characterDisplay;
- if (character != null) {
- if (getCharacterNameDisplay() != null) {
- // Set character display
- characterDisplay = getCharacterNameDisplay();
- } else {
- characterDisplay = character.getDisplayName();
- }
-
- if (dialogue != null && !dialogue.isEmpty()) {
- ImageLoader textbox = new ImageLoader("gui/textbox.png");
- Container textboxMenu = new EmptyContainer(0, 0, configuration.getDialogueBoxWidth(), configuration.getDialogueBoxHeight());
-
- ImageOverlay textBoxImage = new ImageOverlay(textbox, configuration.getDialogueBoxX() + configuration.getDialogueOffsetX(), configuration.getDialogueBoxY() + configuration.getDialogueOffsetY());
- textboxMenu.addOverlay(textBoxImage);
-
- LinkedList texts = StringFormatter.formatText(dialogue);
- TextFlowOverlay textFlowOverlay;
- if (texts.isEmpty()) {
- TextOverlay text = new TextOverlay(dialogue);
- text.setFont(RenJava.CONFIGURATION.getDialogueFont());
- textFlowOverlay = new TextFlowOverlay(text, configuration.getDialogueBoxWidth(), configuration.getDialogueBoxHeight());
- } else {
- textFlowOverlay = new TextFlowOverlay(texts, configuration.getDialogueBoxWidth(), configuration.getDialogueBoxHeight());
- }
- textFlowOverlay.setX(configuration.getTextX() + configuration.getTextOffsetX());
- textFlowOverlay.setY(configuration.getTextY() + configuration.getTextOffsetY());
- textFlowOverlay.setTextFillColor(configuration.getDialogueColor());
- textFlowOverlay.setFont(font);
- textboxMenu.addOverlay(textFlowOverlay);
-
- TextOverlay characterText = new TextOverlay(characterDisplay, new FontLoader(configuration.getCharacterDisplayFont(), configuration.getCharacterTextSize()),
- configuration.getCharacterTextX() + configuration.getCharacterTextOffsetX(),
- configuration.getCharacterTextY() + configuration.getCharacterTextOffsetY());
- characterText.setTextFill(character.getColor());
- characterText.setOrder(DisplayOrder.HIGH);
- textboxMenu.addOverlay(characterText);
-
- container.addContainers(textboxMenu);
- }
- }
+ container.addElement(buildTextBox(character, getCharacterNameDisplay(), dialogue, font));
}
-
-
for (File file : getStyleSheets()) {
try {
RenJava.getInstance().getGameWindow().getScene().getStylesheets().add(file.toURI().toURL().toExternalForm());
diff --git a/src/main/java/me/piitex/renjava/api/scenes/types/InteractableScene.java b/src/main/java/me/piitex/renjava/api/scenes/types/InteractableScene.java
index 264a3c69..15336ee7 100644
--- a/src/main/java/me/piitex/renjava/api/scenes/types/InteractableScene.java
+++ b/src/main/java/me/piitex/renjava/api/scenes/types/InteractableScene.java
@@ -6,7 +6,7 @@
import me.piitex.renjava.events.EventListener;
import me.piitex.renjava.events.types.SceneBuildEvent;
import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.DisplayOrder;
+import me.piitex.renjava.gui.Element;
import me.piitex.renjava.gui.StageType;
import me.piitex.renjava.gui.containers.EmptyContainer;
import me.piitex.renjava.gui.overlays.ImageOverlay;
@@ -21,7 +21,7 @@
*
*
* InteractableScene is designed to be instantiated rather than extended. To create an interactable scene, create a new instance of the InteractableScene class.
- * You can then add overlays to the scene using the {@link #addOverlay(Overlay)} method, which allows you to display buttons, images, and text on top of the background image.
+ * You can then add overlays to the scene using the {@link #addElement(Element)} (Overlay)} method, which allows you to display buttons, images, and text on top of the background image.
* The {@link Overlay} interface provides the necessary methods for positioning the overlays.
*
*
@@ -35,9 +35,9 @@
* Example usage:
* {@code
* InteractableScene scene = new InteractableScene("myScene", backgroundImage);
- * scene.addOverlay(new ButtonOverlay(button));
- * scene.addOverlay(new ImageOverlay(image, x, y));
- * scene.addOverlay(new TextOverlay(text, x, y, xScale, yScale));
+ * scene.addElement(new ButtonOverlay(button));
+ * scene.addElement(new ImageOverlay(image, x, y));
+ * scene.addElement(new TextOverlay(text, x, y, xScale, yScale));
*
* Story story = new Story("myStory");
* story.addScene(scene);
@@ -60,8 +60,6 @@ public class InteractableScene extends RenScene {
private final ImageOverlay backgroundImage;
- private static final RenJava renJava = RenJava.getInstance();
-
@Override
public StageType getStageType() {
return StageType.INTERACTABLE_SCENE;
@@ -104,8 +102,7 @@ public InteractableScene(String id, ImageOverlay backgroundImage) {
public Container build(boolean ui) {
Container container = new EmptyContainer(RenJava.CONFIGURATION.getWidth(), RenJava.CONFIGURATION.getHeight());
if (backgroundImage != null) {
- backgroundImage.setOrder(DisplayOrder.LOW); // Bg should always be at low priority. They will be pushed to the back of the scene.
- container.addOverlays(backgroundImage);
+ container.addElement(backgroundImage);
}
SceneBuildEvent event = new SceneBuildEvent(this, container);
diff --git a/src/main/java/me/piitex/renjava/api/scenes/types/animation/VideoScene.java b/src/main/java/me/piitex/renjava/api/scenes/types/animation/VideoScene.java
index 9e1db84c..312de15b 100644
--- a/src/main/java/me/piitex/renjava/api/scenes/types/animation/VideoScene.java
+++ b/src/main/java/me/piitex/renjava/api/scenes/types/animation/VideoScene.java
@@ -10,7 +10,6 @@
import me.piitex.renjava.api.scenes.text.StringFormatter;
import me.piitex.renjava.configuration.RenJavaConfiguration;
import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.DisplayOrder;
import me.piitex.renjava.gui.StageType;
import me.piitex.renjava.gui.Window;
import me.piitex.renjava.gui.containers.EmptyContainer;
@@ -164,7 +163,7 @@ public Container build(boolean ui) {
mediaOverlay.setHeight(videoHeight);
}
mediaOverlay.setLoop(loop);
- container.addOverlay(mediaOverlay);
+ container.addElement(mediaOverlay);
if (dialogue != null && !dialogue.isEmpty()) {
// Render TextBox
@@ -180,7 +179,7 @@ public Container build(boolean ui) {
Container textboxMenu = new EmptyContainer(0, 0, configuration.getDialogueBoxWidth(), configuration.getDialogueBoxHeight());
ImageOverlay textBoxImage = new ImageOverlay(textbox, configuration.getDialogueBoxX() + configuration.getDialogueOffsetX(), configuration.getDialogueBoxY() + configuration.getDialogueOffsetY());
- textboxMenu.addOverlay(textBoxImage);
+ textboxMenu.addElement(textBoxImage);
LinkedList texts = StringFormatter.formatText(dialogue);
TextFlowOverlay textFlowOverlay;
@@ -195,16 +194,15 @@ public Container build(boolean ui) {
textFlowOverlay.setY(configuration.getTextY() + configuration.getTextOffsetY());
textFlowOverlay.setTextFillColor(configuration.getDialogueColor());
textFlowOverlay.setFont(font);
- textboxMenu.addOverlay(textFlowOverlay);
+ textboxMenu.addElement(textFlowOverlay);
TextOverlay characterText = new TextOverlay(characterDisplay, new FontLoader(configuration.getCharacterDisplayFont(), configuration.getCharacterTextSize()),
configuration.getCharacterTextX() + configuration.getCharacterTextOffsetX(),
configuration.getCharacterTextY() + configuration.getCharacterTextOffsetY());
characterText.setTextFill(character.getColor());
- characterText.setOrder(DisplayOrder.HIGH);
- textboxMenu.addOverlay(characterText);
+ textboxMenu.addElement(characterText);
- container.addContainers(textboxMenu);
+ container.addElement(textboxMenu);
}
}
return container;
diff --git a/src/main/java/me/piitex/renjava/api/scenes/types/choices/ChoiceScene.java b/src/main/java/me/piitex/renjava/api/scenes/types/choices/ChoiceScene.java
index 7c4adf81..e332eb51 100644
--- a/src/main/java/me/piitex/renjava/api/scenes/types/choices/ChoiceScene.java
+++ b/src/main/java/me/piitex/renjava/api/scenes/types/choices/ChoiceScene.java
@@ -8,18 +8,15 @@
import me.piitex.renjava.RenJava;
import me.piitex.renjava.configuration.RenJavaConfiguration;
import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.DisplayOrder;
import me.piitex.renjava.gui.containers.EmptyContainer;
import me.piitex.renjava.gui.containers.ScrollContainer;
import me.piitex.renjava.gui.layouts.VerticalLayout;
import me.piitex.renjava.gui.overlays.ButtonOverlay;
import me.piitex.renjava.gui.overlays.ImageOverlay;
-import me.piitex.renjava.loggers.RenLogger;
import me.piitex.renjava.api.scenes.RenScene;
import me.piitex.renjava.events.types.ChoiceButtonBuildEvent;
import me.piitex.renjava.gui.StageType;
import me.piitex.renjava.api.loaders.ImageLoader;
-import me.piitex.renjava.api.exceptions.ImageNotFoundException;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -175,23 +172,18 @@ public Container build(boolean ui) {
for (Choice choice : choices) {
ButtonOverlay buttonOverlay;
- try {
- buttonOverlay = new ButtonOverlay(choice.getId(), choice.getText(), Color.BLACK, RenJava.CONFIGURATION.getChoiceButtonFont(), 0, 0);
- buttonOverlay.setBorderColor(Color.TRANSPARENT);
- buttonOverlay.setBackgroundColor(Color.TRANSPARENT);
- buttonOverlay.setHover(true);
- buttonOverlay.setTextFill(RenJava.CONFIGURATION.getChoiceButtonColor());
- buttonOverlay.build(); // Sets all the parameters for text
- buildImageButton(buttonOverlay, choice, choiceBoxImage.build()); // Sets all the parameters for the image.
- layout.addOverlays(buttonOverlay);
- } catch (ImageNotFoundException e) {
- RenLogger.LOGGER.error(e.getMessage());
- }
+ buttonOverlay = new ButtonOverlay(choice.getId(), choice.getText(), Color.BLACK, RenJava.CONFIGURATION.getChoiceButtonFont(), 0, 0);
+ buttonOverlay.setBorderColor(Color.TRANSPARENT);
+ buttonOverlay.setBackgroundColor(Color.TRANSPARENT);
+ buttonOverlay.setHover(true);
+ buttonOverlay.setTextFill(RenJava.CONFIGURATION.getChoiceButtonColor());
+ buttonOverlay.build(); // Sets all the parameters for text
+ buildImageButton(buttonOverlay, choice, choiceBoxImage.build()); // Sets all the parameters for the image.
+ layout.addElements(buttonOverlay);
}
- backgroundImage.setOrder(DisplayOrder.LOW);
- menu.addOverlays(backgroundImage);
- menu.addLayout(layout);
+ menu.addElement(backgroundImage);
+ menu.addElement(layout);
}
return menu;
}
diff --git a/src/main/java/me/piitex/renjava/api/scenes/types/input/InputScene.java b/src/main/java/me/piitex/renjava/api/scenes/types/input/InputScene.java
index 6f1230a4..d7991d4a 100644
--- a/src/main/java/me/piitex/renjava/api/scenes/types/input/InputScene.java
+++ b/src/main/java/me/piitex/renjava/api/scenes/types/input/InputScene.java
@@ -1,21 +1,15 @@
package me.piitex.renjava.api.scenes.types.input;
-import javafx.scene.control.TextField;
-import javafx.scene.text.Font;
import javafx.scene.text.Text;
import me.piitex.renjava.RenJava;
import me.piitex.renjava.api.loaders.FontLoader;
import me.piitex.renjava.api.loaders.ImageLoader;
import me.piitex.renjava.api.scenes.RenScene;
import me.piitex.renjava.api.scenes.text.StringFormatter;
-import me.piitex.renjava.api.scenes.types.ImageScene;
import me.piitex.renjava.configuration.RenJavaConfiguration;
-import me.piitex.renjava.events.types.SceneStartEvent;
import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.DisplayOrder;
import me.piitex.renjava.gui.StageType;
-import me.piitex.renjava.gui.Window;
import me.piitex.renjava.gui.containers.EmptyContainer;
import me.piitex.renjava.gui.overlays.*;
import me.piitex.renjava.gui.overlays.events.IInputSetEvent;
@@ -107,41 +101,19 @@ public IInputSetEvent getSetInterface() {
@Override
public Container build(boolean ui) {
Container container = new EmptyContainer(0, 0,1920, 1080);
- loader.setOrder(DisplayOrder.LOW);
- container.addOverlays(loader);
-
+ container.addElement(loader);
if (ui) {
- // Textbox
- ImageLoader textbox = new ImageLoader("gui/textbox.png");
- Container textboxMenu = new EmptyContainer(0, 0, configuration.getDialogueBoxWidth(), configuration.getDialogueBoxHeight());
-
- ImageOverlay textBoxImage = new ImageOverlay(textbox, configuration.getDialogueBoxX() + configuration.getDialogueOffsetX(), configuration.getDialogueBoxY() + configuration.getDialogueOffsetY());
- textboxMenu.addOverlay(textBoxImage);
-
-
- if (text != null && !text.isEmpty()) {
- TextFlowOverlay textFlowOverlay;
- LinkedList texts = StringFormatter.formatText(text);
- if (texts.isEmpty()) {
- Text text1 = new Text(text);
- text1.setFont(RenJava.CONFIGURATION.getDialogueFont().getFont());
- textFlowOverlay = new TextFlowOverlay(text, configuration.getDialogueBoxWidth(), configuration.getDialogueBoxHeight());
- } else {
- textFlowOverlay = new TextFlowOverlay(texts, configuration.getDialogueBoxWidth(), configuration.getDialogueBoxHeight());
- }
- textFlowOverlay.setX(configuration.getTextX() + configuration.getTextOffsetX());
- textFlowOverlay.setY(configuration.getTextY() + configuration.getTextOffsetY());
- textFlowOverlay.setTextFillColor(configuration.getDialogueColor());
-
- inputField = new InputFieldOverlay(defaultInput, 0,0,500,0);
-
+ Container textBox = buildTextBox(null, "", text, font);
+ this.inputField = new InputFieldOverlay(defaultInput, 0,0,500,0);
+ TextFlowOverlay textFlowOverlay = (TextFlowOverlay) textBox.getElements().values().stream().filter(element -> element instanceof TextFlowOverlay).findAny().orElse(null);
+ if (textFlowOverlay != null) {
+ System.out.println("Adding text flow...");
+ textFlowOverlay.add(inputField);
inputField.onInputSetEvent(event -> {
getSetInterface().onInputSet(event);
RenJava.getEventHandler().callEvent(event);
});
- inputField.setOrder(DisplayOrder.HIGH);
-
if (font == null) {
// Default font
font = configuration.getDialogueFont();
@@ -149,13 +121,9 @@ public Container build(boolean ui) {
inputField.setFont(font);
textFlowOverlay.setFont(font);
- textFlowOverlay.add(inputField);
-
- textboxMenu.addOverlay(textFlowOverlay);
-
-
- container.addContainers(textboxMenu);
}
+
+ container.addElement(textBox);
}
return container;
diff --git a/src/main/java/me/piitex/renjava/api/stories/Story.java b/src/main/java/me/piitex/renjava/api/stories/Story.java
index 24519b31..ffba30e4 100644
--- a/src/main/java/me/piitex/renjava/api/stories/Story.java
+++ b/src/main/java/me/piitex/renjava/api/stories/Story.java
@@ -49,9 +49,6 @@
* }
*
*
- *
- * Note: The Story class is now abstract and should be extended to create custom stories.
- *
*
* @see RenScene
* @see ImageScene
@@ -145,6 +142,18 @@ public void start() {
/**
* Clears the existing scenes and initializes the story again by calling the `init()` method.
* This method is useful when you want to reset and update variables.
+ *
+ *
+ * {@code
+ * ImageScene scene = new ImageScene("scene", character, "I'll give you 5$");
+ * scene.onEnd(event -> {
+ * character.addMoney(5);
+ * this.refresh(); // Required to update the 'money' variable for the next scene.
+ * });
+ * ImageScene nextScene = new ImageScene("scene2", character, "Great! I have " + character.getMoney() + " now.");
+ * }
+ *
+ *
* Every time a story starts, it is refreshed.
*/
public void refresh() {
diff --git a/src/main/java/me/piitex/renjava/configuration/InfoFile.java b/src/main/java/me/piitex/renjava/configuration/InfoFile.java
index 5bc68b8d..f690085f 100644
--- a/src/main/java/me/piitex/renjava/configuration/InfoFile.java
+++ b/src/main/java/me/piitex/renjava/configuration/InfoFile.java
@@ -11,9 +11,10 @@
import java.util.Map;
import java.util.Scanner;
-// Custom file that works similar to Properties
-// key=value
-// All string base
+/**
+ * The InfoFile is very similar to {@link java.util.Properties}. It is a very simple and adaptable configuration file.
+ * Does not support nested configurations, maps, or arrays.
+ */
public class InfoFile {
private final File file;
@@ -35,6 +36,11 @@ public InfoFile(File file, boolean create) {
if (file.exists()) {
try {
Scanner scanner = new Scanner(file);
+ // key=value
+ // dependencies=test1,test2
+ // k = dependencies
+ // v = test1,test2
+
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.contains("=")) {
diff --git a/src/main/java/me/piitex/renjava/configuration/RenJavaConfiguration.java b/src/main/java/me/piitex/renjava/configuration/RenJavaConfiguration.java
index 28fb7c4c..5307c531 100644
--- a/src/main/java/me/piitex/renjava/configuration/RenJavaConfiguration.java
+++ b/src/main/java/me/piitex/renjava/configuration/RenJavaConfiguration.java
@@ -1,12 +1,10 @@
package me.piitex.renjava.configuration;
import javafx.scene.paint.Color;
-import me.piitex.renjava.RenJava;
-import me.piitex.renjava.api.exceptions.GameWindowNotSetException;
+import javafx.scene.text.FontPosture;
+import javafx.scene.text.FontWeight;
import me.piitex.renjava.api.loaders.FontLoader;
import me.piitex.renjava.api.loaders.ImageLoader;
-import me.piitex.renjava.gui.Window;
-import me.piitex.renjava.loggers.RenLogger;
import java.util.Map;
@@ -27,6 +25,8 @@ public class RenJavaConfiguration {
private FontLoader uiFont;
private FontLoader characterDisplayFont;
private FontLoader choiceButtonFont;
+ private FontLoader italicFont;
+ private FontLoader boldFont;
private Color dialogueColor = Color.BLACK;
private Color choiceButtonColor = Color.BLACK;
private Color hoverColor = Color.BLUE;
@@ -165,6 +165,28 @@ public void setCharacterDisplayFont(FontLoader characterDisplayFont) {
this.characterDisplayFont = characterDisplayFont;
}
+ public FontLoader getItalicFont() {
+ if (italicFont == null) {
+ return new FontLoader(getDialogueFont().getFont(), FontWeight.NORMAL, FontPosture.ITALIC, getDialogueFont().getSize());
+ }
+ return italicFont;
+ }
+
+ public void setItalicFont(FontLoader italicFont) {
+ this.italicFont = italicFont;
+ }
+
+ public FontLoader getBoldFont() {
+ if (boldFont == null) {
+ return new FontLoader(getDialogueFont().getFont(), FontWeight.BOLD, FontPosture.REGULAR, getDialogueFont().getSize());
+ }
+ return boldFont;
+ }
+
+ public void setBoldFont(FontLoader boldFont) {
+ this.boldFont = boldFont;
+ }
+
public Color getDialogueColor() {
return dialogueColor;
}
diff --git a/src/main/java/me/piitex/renjava/configuration/SettingsProperties.java b/src/main/java/me/piitex/renjava/configuration/SettingsProperties.java
index 57713504..f03888d3 100644
--- a/src/main/java/me/piitex/renjava/configuration/SettingsProperties.java
+++ b/src/main/java/me/piitex/renjava/configuration/SettingsProperties.java
@@ -21,7 +21,7 @@ public class SettingsProperties {
public SettingsProperties() {
File directory = RenJava.getInstance().getBaseDirectory();
- this.file = new File(directory, "settings.properties");
+ this.file = new File(directory, "/renjava/settings.properties");
if (!file.exists()) {
try {
file.createNewFile();
diff --git a/src/main/java/me/piitex/renjava/events/EventHandler.java b/src/main/java/me/piitex/renjava/events/EventHandler.java
index 4935ae37..111d37cc 100644
--- a/src/main/java/me/piitex/renjava/events/EventHandler.java
+++ b/src/main/java/me/piitex/renjava/events/EventHandler.java
@@ -75,8 +75,10 @@ public void callEvent(Event event) {
}
Collection eventListeners = new HashSet<>(registeredListeners);
- for (Addon addon : RenJava.ADDONLOADER.getAddons()) {
- eventListeners.addAll(addon.getRegisteredListeners());
+ if (RenJava.ADDONLOADER != null) {
+ for (Addon addon : RenJava.ADDONLOADER.getAddons()) {
+ eventListeners.addAll(addon.getRegisteredListeners());
+ }
}
invokeEvent(eventListeners, event);
diff --git a/src/main/java/me/piitex/renjava/events/defaults/GameFlowEventListener.java b/src/main/java/me/piitex/renjava/events/defaults/GameFlowEventListener.java
index 1f4eddbc..393ce8ba 100644
--- a/src/main/java/me/piitex/renjava/events/defaults/GameFlowEventListener.java
+++ b/src/main/java/me/piitex/renjava/events/defaults/GameFlowEventListener.java
@@ -22,6 +22,7 @@
import me.piitex.renjava.events.Priority;
import me.piitex.renjava.events.types.*;
import me.piitex.renjava.gui.StageType;
+import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.util.Map;
@@ -65,7 +66,7 @@ public void onMouseClick(MouseClickEvent event) {
}
case PRIMARY -> {
// Go Forward
- playNextScene(event.getEvent().getY());
+ playNextScene(event);
}
case SECONDARY -> {
// Open Main Menu
@@ -85,7 +86,7 @@ public void onMouseClick(MouseClickEvent event) {
}
Container menu = renJava.getMainMenu().mainMenu(true);
- menu.addContainers(renJava.getMainMenu().sideMenu(true));
+ menu.addElement(renJava.getMainMenu().sideMenu(true));
MainMenuBuildEvent buildEvent = new MainMenuBuildEvent(menu);
RenJava.getEventHandler().callEvent(buildEvent);
@@ -115,7 +116,7 @@ public void onMouseClick(MouseClickEvent event) {
RenJava.getEventHandler().callEvent(sceneBuildEvent);
window.clearContainers();
- window.addContainers(menu);
+ window.addContainer(menu);
window.render();
player.setRightClickMenu(false);
@@ -144,7 +145,7 @@ public void onKeyPress(KeyPressEvent event) {
}
if (code == KeyCode.SPACE || code == KeyCode.ENTER) {
- playNextScene();
+ playNextScene(event);
}
if (code == KeyCode.CONTROL) {
@@ -155,7 +156,7 @@ public void onKeyPress(KeyPressEvent event) {
if (story != null && story.getNextScene(currentScene.getId()) != null) {
RenScene nextScene = story.getNextScene(currentScene.getId());
if (RenJava.PLAYER.hasSeenScene(story, nextScene.getId())) {
- playNextScene();
+ playNextScene(event);
}
}
}
@@ -236,11 +237,7 @@ public void onScrollDown(ScrollDownEvent event) {
}
}
- private void playNextScene() {
- playNextScene(-1);
- }
-
- private void playNextScene(double pressedY) {
+ private void playNextScene(@Nullable Event trigger) {
Window window = RenJava.getInstance().getGameWindow();
Player player = RenJava.PLAYER;
RenScene currentScene = player.getCurrentScene();
@@ -263,6 +260,9 @@ private void playNextScene(double pressedY) {
return; // Don't process scene when stopping the transition.
}
+ // Force set the opacity. Fixes issues for fading out transitions.
+ RenJava.getInstance().getGameWindow().getRoot().setOpacity(1);
+
// Next if the scene is an interactable or choice don't process next scene.
if (currentScene instanceof InteractableScene || currentScene instanceof ChoiceScene) {
return;
@@ -270,13 +270,21 @@ private void playNextScene(double pressedY) {
// Lastly, don't process if they click inside the text-field area.
- if (currentScene instanceof InputScene) {
- double textBoxY = RenJava.CONFIGURATION.getTextY();
- if (pressedY > 0) {
- if (pressedY > textBoxY - 200 && pressedY < textBoxY + 200) {
- return;
+ if (currentScene instanceof InputScene inputScene) {
+ if (trigger instanceof MouseClickEvent mouseClickEvent) {
+ double textBoxY = RenJava.CONFIGURATION.getTextY();
+ double pressedY = mouseClickEvent.getEvent().getY();
+ if (pressedY > 0) {
+ if (pressedY > textBoxY - 200 && pressedY < textBoxY + 200) {
+ return;
+ }
}
}
+
+ //FIXME: For some reason getInputField is returning null.
+ if (inputScene.getInputField().getCurrentText().trim().isEmpty()) {
+ return;
+ }
}
if (currentScene != null) {
diff --git a/src/main/java/me/piitex/renjava/events/defaults/MenuClickEventListener.java b/src/main/java/me/piitex/renjava/events/defaults/MenuClickEventListener.java
index eecb47d7..eba3d005 100644
--- a/src/main/java/me/piitex/renjava/events/defaults/MenuClickEventListener.java
+++ b/src/main/java/me/piitex/renjava/events/defaults/MenuClickEventListener.java
@@ -2,12 +2,13 @@
import javafx.application.Platform;
import javafx.scene.control.Button;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import me.piitex.renjava.RenJava;
import me.piitex.renjava.events.types.*;
import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.DisplayOrder;
import me.piitex.renjava.gui.Window;
import me.piitex.renjava.gui.menus.MainMenu;
import me.piitex.renjava.gui.overlays.ButtonOverlay;
@@ -28,11 +29,7 @@ public void onButtonClick(ButtonClickEvent event) {
MainMenu mainMenu = renJava.getMainMenu();
if (button.getId().equalsIgnoreCase("menu-start-button")) {
- RenLogger.LOGGER.info("Creating new game...");
RenJava.PLAYER.resetSession();
- renJava.createBaseData();
- renJava.createStory();
-
// Call GameStartEvent
GameStartEvent event1 = new GameStartEvent(renJava);
RenJava.getEventHandler().callEvent(event1);
@@ -40,16 +37,13 @@ public void onButtonClick(ButtonClickEvent event) {
renJava.start();
}
if (button.getId().equalsIgnoreCase("menu-load-button") && RenJava.PLAYER.getCurrentStageType() != StageType.LOAD_MENU) {
- // Caching the save menu may not be a good idea...
RenJava.PLAYER.setCurrentStageType(StageType.LOAD_MENU);
-
Container load = mainMenu.loadMenu(rightClicked, 1, true); //TODO: Pages
Container side = mainMenu.sideMenu(rightClicked);
- side.setOrder(DisplayOrder.HIGH);
- load.addContainers(side);
+ load.addElement(side, 10);
gameWindow.clearContainers();
- gameWindow.addContainer(load);
+ gameWindow.addContainer(load, 1);
gameWindow.render();
}
@@ -57,22 +51,20 @@ public void onButtonClick(ButtonClickEvent event) {
RenJava.PLAYER.setCurrentStageType(StageType.OPTIONS_MENU);
Container container = mainMenu.settingMenu(rightClicked);
Container side = mainMenu.sideMenu(rightClicked);
- container.addContainer(side);
+ container.addElement(side, 10);
gameWindow.clearContainers();
-
- gameWindow.addContainer(container);
+ gameWindow.addContainer(container, 1);
gameWindow.render();
}
if (button.getId().equalsIgnoreCase("menu-about-button") && RenJava.PLAYER.getCurrentStageType() != StageType.ABOUT_MENU) {
RenJava.PLAYER.setCurrentStageType(StageType.ABOUT_MENU);
Container container = mainMenu.aboutMenu(rightClicked);
+
Container side = mainMenu.sideMenu(rightClicked);
- side.setOrder(DisplayOrder.HIGH);
- container.addContainer(side);
+ container.addElement(side); // Should be able to add in the correct order without the need to specify. Will look into it later.
gameWindow.clearContainers();
-
gameWindow.addContainer(container);
gameWindow.render();
}
@@ -81,18 +73,16 @@ public void onButtonClick(ButtonClickEvent event) {
Container menu = mainMenu.loadMenu(rightClicked,1, false); // Builds first page
Container side = mainMenu.sideMenu(rightClicked);
- side.setOrder(DisplayOrder.HIGH);
- menu.addContainers(side);
+ menu.addElement(side, 10);
gameWindow.clearContainers();
-
- gameWindow.addContainer(menu);
+ gameWindow.addContainer(menu, 10);
gameWindow.render();
}
if (button.getId().equalsIgnoreCase("menu-quit-button")) {
// Prompt before exiting...
RenJava.ADDONLOADER.disable();
- Platform.exit();
+ RenJava.shutdown();
}
@@ -117,17 +107,16 @@ public void onButtonClick(ButtonClickEvent event) {
Container menu = mainMenu.mainMenu(false);
Container side = mainMenu.sideMenu(false);
RenJava.PLAYER.resetSession();
- side.setOrder(DisplayOrder.HIGH);
- menu.addContainer(side);
+ menu.addElement(side, 2);
gameWindow.clearContainers();
- gameWindow.addContainers(menu);
+ gameWindow.addContainer(menu, 1);
gameWindow.render();
// Close the prompt
prompt.closeWindow();
});
- prompt.addOverlay(confirm);
+ prompt.addElement(confirm);
ButtonOverlay cancel = new ButtonOverlay("cancel", "Cancel", Color.WHITE, RenJava.CONFIGURATION.getUiFont());
cancel.setX(700);
@@ -138,7 +127,7 @@ public void onButtonClick(ButtonClickEvent event) {
window.close();
});
- prompt.addOverlay(cancel);
+ prompt.addElement(cancel);
prompt.render();
} else {
@@ -147,13 +136,21 @@ public void onButtonClick(ButtonClickEvent event) {
Container menu = mainMenu.mainMenu(false);
Container side = mainMenu.sideMenu(false);
RenJava.PLAYER.resetSession();
- side.setOrder(DisplayOrder.HIGH);
- menu.addContainer(side);
+ menu.addElement(side, 2);
gameWindow.clearContainers();
- gameWindow.addContainers(menu);
+ gameWindow.addContainer(menu, 1);
gameWindow.render();
}
}
}
+ @Listener
+ public void onDebugControl(KeyPressEvent event) {
+ KeyEvent keyEvent = event.getEvent();
+ if (keyEvent.isControlDown() && keyEvent.isShiftDown() && keyEvent.getCode() == KeyCode.R) {
+ RenLogger.LOGGER.info("Reloading game...");
+ renJava.reload(RenJava.PLAYER.inMenu()); // Reload graphics if they are in the main menu.
+ }
+ }
+
}
diff --git a/src/main/java/me/piitex/renjava/events/defaults/ScenesEventListener.java b/src/main/java/me/piitex/renjava/events/defaults/ScenesEventListener.java
index f65dfbe8..5bc2896a 100644
--- a/src/main/java/me/piitex/renjava/events/defaults/ScenesEventListener.java
+++ b/src/main/java/me/piitex/renjava/events/defaults/ScenesEventListener.java
@@ -27,9 +27,6 @@ public void onSceneEndEvent(SceneEndEvent endEvent) {
if (scene instanceof VideoScene videoScene) {
videoScene.stop();
}
-
- // Fix opacity issues caused by improperly applied transitions.
- RenJava.getInstance().getGameWindow().getRoot().setOpacity(1);
}
@Listener(priority = Priority.HIGHEST)
diff --git a/src/main/java/me/piitex/renjava/gui/Container.java b/src/main/java/me/piitex/renjava/gui/Container.java
index 8613f345..9062fda0 100644
--- a/src/main/java/me/piitex/renjava/gui/Container.java
+++ b/src/main/java/me/piitex/renjava/gui/Container.java
@@ -5,9 +5,9 @@
import me.piitex.renjava.gui.layouts.Layout;
import me.piitex.renjava.gui.overlays.Overlay;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.util.*;
/**
* The container houses all the elements that render onto the {@link Window}. The class can be extended to support different containers that can handle rendering differently.
@@ -31,30 +31,25 @@
* }
*
*/
-public abstract class Container {
+public abstract class Container extends Renderer {
private double x, y;
- private final double width, height;
- private DisplayOrder order;
+ private final List stylesheets = new ArrayList<>();
- // Might be better to index that way someone can modify the index and move overlays around.
- private final LinkedList overlays = new LinkedList<>();
- private final LinkedList layouts = new LinkedList<>();
- private final LinkedList containers = new LinkedList<>();
-
- public Container(double x, double y, double width, double height) {
+ public Container(Node view, double x, double y, double width, double height) {
+ setNode(view);
this.x = x;
this.y = y;
- this.width = width;
- this.height = height;
- this.order = DisplayOrder.NORMAL;
+ setWidth(width);
+ setHeight(height);
}
- public Container(double x, double y, double width, double height, DisplayOrder order) {
+ public Container(Node view, double x, double y, double width, double height, int index) {
+ setNode(view);
this.x = x;
this.y = y;
- this.width = width;
- this.height = height;
- this.order = order;
+ setWidth(width);
+ setHeight(height);
+ setIndex(index);
}
/**
@@ -87,67 +82,17 @@ public void setY(double y) {
this.y = y;
}
- /**
- * @return The width of the container.
- */
- public double getWidth() {
- return width;
- }
-
- /**
- * @return The height of the container.
- */
- public double getHeight() {
- return height;
- }
-
- /**
- * {@link DisplayOrder} is used to order the stacking of elements or containers.
- * @return The display order of the container.
- */
- public DisplayOrder getOrder() {
- return order;
- }
-
- /**
- * Sets the {@link DisplayOrder} for the container.
- * @param order The {@link DisplayOrder}.
- */
- public void setOrder(DisplayOrder order) {
- this.order = order;
- }
-
- /**
- * Adds a singular {@link Overlay} to the container.
- * @param overlay The {@link Overlay} to be added.
- */
- public void addOverlay(Overlay overlay) {
- this.overlays.add(overlay);
- }
-
- /**
- * Adds an array of {@link Overlay}s to the container. The overlays are positioned by the order of the array.
- * The first overlay of the array is the first to be added.
- * @param overlays An array of {@link Overlay}s to be added.
- */
- public void addOverlays(Overlay... overlays) {
- this.overlays.addAll(List.of(overlays));
- }
-
- /**
- * Adds a linked list of {@link Overlay}s to the container.
- * @param overlays The list of {@link Overlay}s to be added.
- */
- public void addOverlays(LinkedList overlays) {
- this.overlays.addAll(overlays);
- }
-
/**
* Gets all {@link Overlay}s added to the container.
* @return The current linked list of {@link Overlay}s.
*/
public LinkedList getOverlays() {
- return overlays;
+ LinkedList toReturn = new LinkedList<>();
+ getElements().values().stream().filter(element -> element instanceof Overlay).forEach(element -> {
+ Overlay overlay = (Overlay) element;
+ toReturn.add(overlay);
+ });
+ return toReturn;
}
/**
@@ -155,24 +100,12 @@ public LinkedList getOverlays() {
* @return The current linked list of sub-containers.
*/
public LinkedList getContainers() {
- return containers;
- }
-
- /**
- * Adds a container as a sub-container to this container.
- * @param container The sub-container to be added.
- */
- public void addContainer(Container container) {
- this.containers.add(container);
- }
-
- /**
- * Adds an array of sub-containers to be added. The containers are positioned by the order of the array.
- * The first container of the array is the first to be added.
- * @param containers An array of containers to be added.
- */
- public void addContainers(Container... containers) {
- this.containers.addAll(List.of(containers));
+ LinkedList toReturn = new LinkedList<>();
+ getElements().values().stream().filter(element -> element instanceof Container).forEach(element -> {
+ Container container = (Container) element;
+ toReturn.add(container);
+ });
+ return toReturn;
}
/**
@@ -180,72 +113,29 @@ public void addContainers(Container... containers) {
* @return The current linked list of {@link Layout}s
*/
public LinkedList getLayouts() {
- return layouts;
- }
-
- /**
- * Adds a {@link Layout} to be added to the container.
- * @param layout The {@link Layout} to be added.
- */
- public void addLayout(Layout layout) {
- this.layouts.add(layout);
- }
-
- /**
- * Adds an array of {@link Layout}s to be added. The layouts are positioned by the order of the array.
- * The first layout of the array is the first to be added.
- * @param layouts An array {@link Layout}s to be added.
- */
- public void addLayouts(Layout... layouts) {
- this.layouts.addAll(List.of(layouts));
- }
-
- /**
- * This should only be used by the engine. This builds the overlays, layouts, and containers.
- * It translates the RenJava API into JavaFX by converting the gui components into {@link Node}s.
- * @param lowOrder The list of low order nodes.
- * @param normalOrder The list of normal order nodes.
- * @param highOrder The list of high order nodes.
- */
- public void buildBase(LinkedList lowOrder, LinkedList normalOrder, LinkedList highOrder) {
- for (Overlay overlay : getOverlays()) {
- Node node = overlay.render();
- if (node != null) {
- if (overlay.getOrder() == DisplayOrder.LOW) {
- lowOrder.add(node);
- } else if (overlay.getOrder() == DisplayOrder.NORMAL) {
- normalOrder.add(node);
- } else if (overlay.getOrder() == DisplayOrder.HIGH) {
- highOrder.add(node);
- }
- }
- }
-
- for (Layout layout : getLayouts()) {
- Node node = layout.render(this);
- if (node != null) {
- if (layout.getOrder() == DisplayOrder.LOW) {
- lowOrder.add(node);
- } else if (layout.getOrder() == DisplayOrder.NORMAL) {
- normalOrder.add(node);
- } else if (layout.getOrder() == DisplayOrder.HIGH) {
- highOrder.add(node);
- }
- }
+ LinkedList toReturn = new LinkedList<>();
+ getElements().values().stream().filter(element -> element instanceof Layout).forEach(element -> {
+ Layout layout = (Layout) element;
+ toReturn.add(layout);
+ });
+ return toReturn;
+ }
+
+ public void addStyleSheet(File file) {
+ try {
+ stylesheets.add(file.toURI().toURL().toExternalForm());
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
}
+ }
- lowOrder.addAll(normalOrder);
- lowOrder.addAll(highOrder);
-
- // Render sub containers
- for (Container container : getContainers()) {
- lowOrder.addAll(container.build().getValue()); // Might not work
- }
+ public List getStylesheets() {
+ return stylesheets;
}
/**
- * Builds and assembles the container.
+ * Builds and assembles the container. Converts RenJava API into JavaFX.
* @return An entry set where the key is the pane as a node. The value is the collection of nodes which the pane contains.
*/
- public abstract Map.Entry> build();
+ public abstract Node build();
}
diff --git a/src/main/java/me/piitex/renjava/gui/DisplayOrder.java b/src/main/java/me/piitex/renjava/gui/DisplayOrder.java
deleted file mode 100644
index fea6e588..00000000
--- a/src/main/java/me/piitex/renjava/gui/DisplayOrder.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package me.piitex.renjava.gui;
-
-// This is not very practical. Easy yes. Good no. I have an idea to switch this to indexes (0 is back inf is front)
-public enum DisplayOrder {
- HIGH,
- NORMAL,
- LOW
-}
diff --git a/src/main/java/me/piitex/renjava/gui/Element.java b/src/main/java/me/piitex/renjava/gui/Element.java
new file mode 100644
index 00000000..1ba2aa88
--- /dev/null
+++ b/src/main/java/me/piitex/renjava/gui/Element.java
@@ -0,0 +1,86 @@
+package me.piitex.renjava.gui;
+
+import javafx.scene.Node;
+import me.piitex.renjava.gui.layouts.Layout;
+import me.piitex.renjava.gui.overlays.ImageOverlay;
+import me.piitex.renjava.gui.overlays.Overlay;
+import me.piitex.renjava.gui.overlays.TextOverlay;
+
+/**
+ * Represents a graphical element that can be rendered to the {@link Window} or a {@link Container}.
+ *
+ * This is an abstract base class for all renderable elements in the GUI framework.
+ * Elements are organized by their rendering index, which determines the order in which
+ * they are drawn. A lower index means the element will be rendered earlier (underneath others).
+ *
+ *
+ * @see Container
+ * @see Overlay
+ * @see Layout
+ */
+public abstract class Element {
+ private int index = 0;
+ private boolean enabled = true;
+ private Node node; // Underlying JavaFX component
+
+
+ /**
+ * Retrieves the rendering index of this element.
+ *
+ * @return The rendering index of the element. A lower value means the element is rendered earlier. An index of 0 results in automatic assignment. Use '1' as the lowest layer.
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Sets the rendering index of this element.
+ *
+ * An index of 1 will cause the element to be rendered first (at the bottom layer).
+ * Higher index values will render the element on top of those with lower indices. An index of 0 results in automatic assignment.
+ *
+ *
+ * @param index The new rendering index for the element.
+ */
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ if (getNode() != null) {
+ getNode().setDisable(!enabled);
+ } else {
+// NodeNotDefinedException exception = new NodeNotDefinedException(this);
+// logger.error(exception.getMessage(), exception);
+ }
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public Node getNode() {
+ return node;
+ }
+
+ public void setNode(Node node) {
+ this.node = node;
+ }
+
+ /**
+ * Assembles the element into its JavaFX {@link Node}.
+ *
+ *
+ * For {@link Overlay}'s it will return the render functions. Examples: {@link TextOverlay#render()}, {@link ImageOverlay#render()}
+ *
+ *
+ * For {@link Layout}'s it will return the {@link Layout#render()} result.
+ *
+ *
+ * For {@link Container}'s it will return the {@link Container#build()} result.
+ *
+ * @return The constructed node.
+ */
+ public abstract Node assemble();
+}
diff --git a/src/main/java/me/piitex/renjava/gui/GuiLoader.java b/src/main/java/me/piitex/renjava/gui/GuiLoader.java
index 3969072a..cf33ff95 100644
--- a/src/main/java/me/piitex/renjava/gui/GuiLoader.java
+++ b/src/main/java/me/piitex/renjava/gui/GuiLoader.java
@@ -3,16 +3,13 @@
import javafx.animation.PauseTransition;
import javafx.application.HostServices;
-import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
import me.piitex.renjava.RenJava;
import me.piitex.renjava.api.loaders.ImageLoader;
-import me.piitex.renjava.gui.containers.EmptyContainer;
import me.piitex.renjava.gui.menus.DefaultMainMenu;
import me.piitex.renjava.gui.menus.MainMenu;
-import me.piitex.renjava.gui.overlays.ImageOverlay;
import me.piitex.renjava.loggers.RenLogger;
import me.piitex.renjava.api.loaders.FontLoader;
import me.piitex.renjava.configuration.RenJavaConfiguration;
@@ -41,7 +38,9 @@ public GuiLoader(Stage stage, RenJava renJava, HostServices services) {
private void buildSplashScreen() {
RenLogger.LOGGER.info("Creating Splash screen...");
- stage.initStyle(StageStyle.UNDECORATED);
+ try {
+ stage.initStyle(StageStyle.UNDECORATED);
+ } catch (IllegalStateException ignored) {}
// Update Stage
RenJava.PLAYER.setCurrentStageType(StageType.MAIN_MENU);
@@ -105,7 +104,10 @@ private void buildMainMenu() {
RenLogger.LOGGER.info("Rendering main menu...");
// When building title screen create a new window and eventually store the window for easy access
- Window window = new Window(RenJava.CONFIGURATION.getGameTitle(), StageStyle.DECORATED, new ImageLoader("gui/window_icon.png"));
+ Window window = new WindowBuilder(RenJava.CONFIGURATION.getGameTitle()).setStageStyle(StageStyle.DECORATED).setIcon(new ImageLoader("gui/window_icon.png")).build();
+ window.setMaximized(configuration.isMaximizedGameWindow());
+ renJava.setGameWindow(window);
+
// Specifically for the gameWindow it is needed to setup the shutdown events.
window.getStage().setOnHiding(windowEvent -> {
ShutdownEvent shutdownEvent = new ShutdownEvent();
@@ -135,13 +137,9 @@ private void buildMainMenu() {
}
}
- Platform.exit();
- System.exit(0);
+ RenJava.shutdown();
});
-
- renJava.setGameWindow(window);
-
MainMenu menu = renJava.getMainMenu();
@@ -155,20 +153,10 @@ private void buildMainMenu() {
// Render main menu
Container container = menu.mainMenu(false);
- MainMenuBuildEvent event = new MainMenuBuildEvent(container);
- RenJava.getEventHandler().callEvent(event);
window.addContainer(container);
- Container sideMenu = menu.sideMenu(false);
-
- window.addContainers(sideMenu);
-
- MainMenuDispatchEvent dispatchEvent = new MainMenuDispatchEvent(container);
- RenJava.getEventHandler().callEvent(dispatchEvent);
-
- window.setMaximized(configuration.isMaximizedGameWindow());
-
- window.render(); // Renders the window
+ Container sideBar = menu.sideMenu(false);
+ container.addElement(sideBar);
MainMenuRenderEvent renderEvent = new MainMenuRenderEvent(container, false);
RenJava.getEventHandler().callEvent(renderEvent);
diff --git a/src/main/java/me/piitex/renjava/gui/Renderer.java b/src/main/java/me/piitex/renjava/gui/Renderer.java
new file mode 100644
index 00000000..79db5cfd
--- /dev/null
+++ b/src/main/java/me/piitex/renjava/gui/Renderer.java
@@ -0,0 +1,349 @@
+package me.piitex.renjava.gui;
+
+import javafx.scene.Node;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.Region;
+import javafx.scene.paint.Color;
+import me.piitex.renjava.gui.layouts.Layout;
+import me.piitex.renjava.gui.overlays.ImageOverlay;
+import me.piitex.renjava.gui.overlays.Overlay;
+
+import java.util.*;
+
+/**
+ * An element which handles rendering of {@link Container}, {@link Layout}, and {@link Overlay}
+ *
+ * @see Layout
+ * @see Container
+ */
+public class Renderer extends Element {
+ private final TreeMap elements = new TreeMap<>();
+ private double width, height;
+ private double prefWidth, prefHeight;
+ private double maxWidth, maxHeight;
+ private double xOffset = 0, yOffset = 0;
+ private Color backgroundColor;
+ private Color borderColor;
+ private double borderWidth = 1;
+ private final List styles = new ArrayList<>();
+
+
+ public double getWidth() {
+ return width;
+ }
+
+ public void setWidth(double width) {
+ this.width = width;
+ if (getNode() instanceof Region region) {
+ region.setMinWidth(width);
+ }
+ }
+
+ public double getHeight() {
+ return height;
+ }
+
+ public void setHeight(double height) {
+ this.height = height;
+
+ if (getNode() instanceof Region region) {
+ region.setMinHeight(height);
+ }
+ }
+
+ public double getPrefWidth() {
+ return prefWidth;
+ }
+
+ public double getPrefHeight() {
+ return prefHeight;
+ }
+
+ public void setPrefWidth(double prefWidth) {
+ this.prefWidth = prefWidth;
+
+ if (getNode() instanceof Region region) {
+ region.setPrefWidth(height);
+ }
+ }
+
+ public void setPrefHeight(double prefHeight) {
+ this.prefHeight = prefHeight;
+
+ if (getNode() instanceof Region region) {
+ region.setPrefHeight(height);
+ }
+ }
+
+ public void setPrefSize(double width, double height) {
+ this.prefWidth = width;
+ this.prefHeight = height;
+
+ if (getNode() instanceof Region region) {
+ region.setPrefSize(width, height);
+ }
+ }
+
+ public double getMaxWidth() {
+ return maxWidth;
+ }
+
+ public double getMaxHeight() {
+ return maxHeight;
+ }
+
+ public void setMaxSize(double width, double height) {
+ this.maxWidth = width;
+ this.maxHeight = height;
+
+ if (getNode() instanceof Region region) {
+ region.setMaxSize(width, height);
+ }
+ }
+
+ public Color getBackgroundColor() {
+ return backgroundColor;
+ }
+
+ public void setBackgroundColor(Color backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ }
+
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ this.borderColor = borderColor;
+ }
+
+ public double getBorderWidth() {
+ return borderWidth;
+ }
+
+ public void setBorderWidth(double borderWidth) {
+ this.borderWidth = borderWidth;
+ }
+
+ public List getStyles() {
+ return styles;
+ }
+
+ public void addStyle(String style) {
+ styles.add(style);
+ }
+
+ public TreeMap getElements() {
+ return elements;
+ }
+
+ /**
+ * Retrieves the current element at the specific index. If the element is not present this will return null.
+ * @param index Position of the desired element.
+ * @return The {@link Element}
+ */
+ public Element getElementAt(int index) {
+ return elements.get(index);
+ }
+
+ /**
+ * Adds an element to the container. The added element will be indexed to the front of the container.
+ * @param element The {@link Element} to be added.
+ */
+ public void addElement(Element element) {
+ int index = element.getIndex();
+ if (index == 0) {
+ index = elements.size();
+ element.setIndex(index);
+ }
+
+ addElement(element, index);
+ }
+
+ /**
+ * Adds the element to the specific index. If there is an element already bound to that index it is shuffled forward.
+ *
+ * @param element The {@link Element} to add to the container.
+ * @param index The index/order of the element.
+ */
+ public void addElement(Element element, int index) {
+ Element current = elements.get(index);
+ if (current != null) {
+ int i = index + 1;
+ addElement(getElementAt(index), i);
+ }
+ element.setIndex(index);
+ elements.put(index, element);
+
+ Node node = element.assemble();
+ element.setNode(node);
+
+ if (element instanceof ImageOverlay imageOverlay) {
+ System.out.println("Adding image: " + imageOverlay.getPath());
+ }
+
+ // Ensure JavaFX operations are being executed on the FXThread.
+ addToView(node, index);
+ }
+
+ /**
+ * Adds an array of elements to the container. The elements are positioned by the order of the array.
+ * The added elements will be indexed to the front of the container.
+ * @param elements The array of {@link Element}s to be added.
+ */
+ public void addElements(Element... elements) {
+ for (Element element : elements) {
+ addElement(element);
+ }
+ }
+
+ /**
+ * Adds a {@link LinkedList } of elements to the container. The elements are position by the order of the list.
+ * @param elements The list of elements to be added.
+ */
+ public void addElements(LinkedList elements) {
+ for (Element element : elements) {
+ addElement(element);
+ }
+ }
+
+ public void removeElement(int index) {
+ removeElement(getElementAt(index));
+ }
+
+ public void removeElement(Element element) {
+ elements.remove(element.getIndex());
+ if (element instanceof Overlay overlay) {
+ removeFromView(overlay.getNode());
+ }
+ if (element instanceof Renderer renderer) {
+ removeFromView(renderer.getNode());
+ }
+ }
+
+ public void removeAllElement(Element element) {
+ LinkedHashMap toRemove = new LinkedHashMap<>(elements);
+ toRemove.forEach((integer, e) -> {
+ if (e == element) {
+ removeElement(e);
+ }
+ });
+ }
+
+ public void moveElement(int oldIndex, int newIndex) {
+ Element element = elements.get(oldIndex);
+ if (element != null) {
+ elements.put(newIndex, element);
+ elements.remove(oldIndex);
+ }
+ }
+
+ public void removeAllElements() {
+ elements.clear();
+ if (getNode() instanceof Pane pane) {
+ pane.getChildren().clear();
+ }
+ }
+
+ public void replaceElement(int index, Element element) {
+ if (getNode() instanceof Pane pane) {
+ pane.getChildren().remove(index);
+ pane.getChildren().add(index, element.assemble());
+ elements.replace(index, element);
+ }
+ }
+
+ public void addToView(Node node, int index) {
+ System.out.println("Adding '" + node.getClass().getName() + "' to '" + getNode().getClass().getName() + "' at slot '" + index + "'");
+ if (getNode() instanceof Pane pane) {
+ if (!pane.getChildren().contains(node)) {
+ pane.getChildren().add(node);
+ }
+ } else {
+ Throwable throwable = new Throwable();
+ throwable.printStackTrace();
+ }
+ }
+
+ public void removeFromView(Node node) {
+ if (getNode() instanceof Pane pane) {
+ pane.getChildren().remove(node);
+ }
+ }
+
+ public double getOffsetX() {
+ return xOffset;
+ }
+
+ public void setOffsetX(double xOffset) {
+ this.xOffset = xOffset;
+ }
+
+ public double getOffsetY() {
+ return yOffset;
+ }
+
+ public void setOffsetY(double yOffset) {
+ this.yOffset = yOffset;
+ }
+
+ public void setStyling(Node node) {
+ node.getStyleClass().addAll(styles);
+ if (node instanceof Region region) {
+ StringBuilder inLineCss = new StringBuilder();
+ if (backgroundColor != null) {
+ inLineCss.append("-fx-background-color: ").append(cssColor(backgroundColor)).append("; ");
+ }
+
+ if (borderColor != null) {
+ inLineCss.append("-fx-border-color: ").append(cssColor(borderColor)).append("; ");
+ inLineCss.append("-fx-border-width: ").append(borderWidth).append(" ").append(borderWidth).append(" ").append(borderWidth).append(" ").append(borderWidth).append("; ");
+ inLineCss.append("-fx-border-style: ").append("solid").append("; ");
+ }
+
+ region.setStyle(inLineCss.toString());
+ }
+ }
+
+ private void updateOffsets(Node node) {
+ if (getOffsetX() > 0 || getOffsetY() > 0) {
+ node.setTranslateX(node.getTranslateX() + getOffsetX());
+ node.setTranslateY(node.getTranslateY() + getOffsetY());
+ }
+ }
+
+ // Helper css function
+ private String cssColor(Color color) {
+ return String.format("rgba(%d, %d, %d, %f)",
+ (int) (255 * color.getRed()),
+ (int) (255 * color.getGreen()),
+ (int) (255 * color.getBlue()),
+ color.getOpacity());
+ }
+
+
+ @Override
+ public Node assemble() {
+ Node node = null;
+ if (this instanceof Container container) {
+ node = container.build();
+ }
+ if (this instanceof Layout layout) {
+ node = layout.render();
+ }
+
+ // Assemble existing elements.
+ if (node instanceof Pane pane) {
+ for (Element element : getElements().values()) {
+ Node child = element.assemble();
+ if (element instanceof Overlay overlay) {
+ overlay.setNode(child);
+ }
+ if (!pane.getChildren().contains(child)) {
+ pane.getChildren().add(child);
+ }
+ }
+ }
+ return node;
+ }
+}
diff --git a/src/main/java/me/piitex/renjava/gui/Window.java b/src/main/java/me/piitex/renjava/gui/Window.java
index b877d27a..48158c09 100644
--- a/src/main/java/me/piitex/renjava/gui/Window.java
+++ b/src/main/java/me/piitex/renjava/gui/Window.java
@@ -1,260 +1,141 @@
package me.piitex.renjava.gui;
+import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.Image;
-import javafx.scene.input.KeyCode;
-import javafx.scene.input.KeyEvent;
-import javafx.scene.input.ScrollEvent;
-import javafx.scene.layout.*;
+import javafx.scene.layout.Background;
+import javafx.scene.layout.BackgroundFill;
+import javafx.scene.layout.CornerRadii;
+import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.transform.Scale;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
-import me.piitex.renjava.RenJava;
import me.piitex.renjava.api.loaders.ImageLoader;
import me.piitex.renjava.api.scenes.RenScene;
import me.piitex.renjava.api.scenes.transitions.Transitions;
-import me.piitex.renjava.events.types.*;
-import me.piitex.renjava.api.exceptions.ImageNotFoundException;
import me.piitex.renjava.gui.layouts.Layout;
import me.piitex.renjava.gui.overlays.Overlay;
-import me.piitex.renjava.loggers.RenLogger;
-import me.piitex.renjava.utils.KeyUtils;
-import me.piitex.renjava.tasks.Tasks;
-import me.piitex.renjava.utils.ModifierKeyList;
-
-import java.time.Duration;
-import java.time.Instant;
-import java.util.Arrays;
+
import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
/**
- * Window is the main GUI component which handle the rendering process for the engine. There are three components to windows which are {@link Container}, {@link Overlay}, {@link Layout}.
- * Window houses and manages these components.
- *
- * You can create and render multiple windows at once. The title is used for the process name and label in the top left corner.
- * The stage style is used to control how the window is displayed. A decorated style will contain a "X", minimize, and maximize button.
- * An undecorated style will not contain any top bar similar to a full-screen game.
- *
- * {@code
- * Window window = new Window("Window Title", StageStyle.DECORATED, new ImageLoader("path/to/icon.png"));
- * }
- *
- *
- * To display various elements to a window you must create a container first. Once the container is created you simply have to add it the window.
- * Note, you can add and position multiple containers to a single window.
- *
- * {@code
- * Window window = application.getWindow();
- * Container container = new EmptyContainer(x, y, width, height);
- * window.addContainer(container);
- * }
- *
- *
- * There is no game loop which handles rendering. All the rendering is handled by JavaFX which does it automatically when a node is modified.
- * To change or render a different container, you must remove the current containers and re-render the window.
- *
- * {@code
- * Window window = application.getWindow();
- * window.clearContainers(); // Clear existing containers
+ * The Window serves as the primary GUI component, managing the rendering process for the engine.
+ * It houses and manages three core components: {@link Container}, {@link Overlay}, and {@link Layout}.
*
- * window.addContainer(newContainer);
+ * Multiple windows can be created and rendered simultaneously. The window's title serves as its process name and label.
+ * The stage style dictates the window's appearance, with {@link StageStyle#DECORATED} providing standard
+ * window controls (close, minimize, maximize) and {@link StageStyle#UNDECORATED} removing the title bar
+ * for a borderless experience, often used in full-screen applications.
+ *
+ * {@code
+ * Window window = new WindowBuilder("My Application Window")
+ * .setStageStyle(StageStyle.UNDECORATED)
+ * .setDimensions(800, 600)
+ * .setBackgroundColor(Color.BLACK)
+ * .build();
+ * }
*
- * window.render(); // Process newly added container
- * }
- *
*
- * RenJava framework handles the game window which you can access via the {@link RenJava} class.
- * It is recommended to have your own application instance but isn't necessarily required.
- * You can modify the game window at any point pass the initial loading stage.
- *
- * {@code
- * Window gameWindow = RenJava.getInstance().getGameWindow();
- * }
- *
+ * To display elements within a window, a {@link Container} must first be created and added to the window.
+ * Multiple containers can be added and positioned within a single window.
+ * {@code
+ * Window window = application.getWindow();
+ * Container container = new EmptyContainer(x, y, width, height);
+ * window.addContainer(container);
+ * }
+ *
*
- * All GUI related functions must be called in the JavaFX thread. You can use the {@link Tasks} utility to switch between different threads.
- *
- * {@code
- * Tasks.runAsync(() -> {
- * // Some code to be ran asynchronously
+ * All GUI-related functions, especially those involving scene graph modifications,
+ * must be executed on the JavaFX Application Thread.
+ * {@code
+ * new Thread( () -> {
+ * // Code to be ran asynchronously
+ * loadBackend();
*
- * // Handle JavaFX in async
- * Tasks.runJavaFXThread(() -> {
- * // Gui related code
- * window.render();
- * })
- * })
- * }
- *
+ * Platform.runLater( () -> {
+ * // Any gui related code.
+ * initializeProgressIndicator();
+ * })
+ * })
+ * }
*
* @see Container
* @see Overlay
* @see Layout
- * @see Tasks
- * @see RenJava#getGameWindow()
+ * @see Platform#runLater(Runnable)
*/
public class Window {
private final String title;
private final ImageLoader icon;
private final StageStyle stageStyle;
- private int width, height;
- private boolean fullscreen = false, maximized = false;
- private Color backgroundColor = Color.BLACK;
+ private double initialWidth, initialHeight;
+ private double width, height;
+ private boolean fullscreen, maximized ;
+ private Color backgroundColor;
private Stage stage;
private Scene scene;
private Pane root;
- // Time tracking for thresholds
- private Instant lastRun;
- private Instant firstRun;
- private boolean captureInput = true;
-
- private LinkedList containers = new LinkedList<>();
-
- private boolean focused = true;
-
- public Window(String title, StageStyle stageStyle, ImageLoader icon) {
- this.width = RenJava.CONFIGURATION.getWidth();
- this.height = RenJava.CONFIGURATION.getHeight();
- this.title = title;
- this.stageStyle = stageStyle;
- this.icon = icon;
- buildStage();
- }
-
- public Window(String title, StageStyle stageStyle, ImageLoader icon, boolean captureInput) {
- this.width = RenJava.CONFIGURATION.getWidth();
- this.height = RenJava.CONFIGURATION.getHeight();
- this.captureInput = captureInput;
- this.title = title;
- this.stageStyle = stageStyle;
- this.icon = icon;
- buildStage();
- }
-
- public Window(String title, StageStyle stageStyle, boolean fullscreen, boolean maximized, ImageLoader icon) {
- this.width = RenJava.CONFIGURATION.getWidth();
- this.height = RenJava.CONFIGURATION.getHeight();
- this.title = title;
- this.stageStyle = stageStyle;
- this.icon = icon;
- this.setFullscreen(fullscreen);
- this.setMaximized(maximized);
- buildStage();
- }
-
- public Window(String title, StageStyle stageStyle, boolean fullscreen, boolean maximized, boolean captureInput, ImageLoader icon) {
- this.width = RenJava.CONFIGURATION.getWidth();
- this.height = RenJava.CONFIGURATION.getHeight();
- this.captureInput = captureInput;
- this.title = title;
- this.stageStyle = stageStyle;
- this.icon = icon;
- this.setFullscreen(fullscreen);
- this.setMaximized(maximized);
- buildStage();
- }
-
- public Window(String title, StageStyle stageStyle, ImageLoader icon, int width, int height) {
- this.title = title;
- this.stageStyle = stageStyle;
- this.icon = icon;
- this.width = width;
- this.height = height;
- buildStage();
- }
-
- public Window(String title, StageStyle stageStyle, ImageLoader icon, int width, int height, boolean captureInput) {
- this.title = title;
- this.stageStyle = stageStyle;
- this.icon = icon;
- this.width = width;
- this.height = height;
- this.captureInput = captureInput;
- buildStage();
- }
+ private final boolean scale;
+ private final boolean focused;
- public Window(String title, Color backgroundColor, StageStyle stageStyle, ImageLoader icon) {
- this.width = RenJava.CONFIGURATION.getWidth();
- this.height = RenJava.CONFIGURATION.getHeight();
- this.title = title;
- this.backgroundColor = backgroundColor;
- this.stageStyle = stageStyle;
- this.icon = icon;
- buildStage();
- }
+ private TreeMap containers = new TreeMap<>();
+ private Container currentPopup = null;
- public Window(String title, Color backgroundColor, StageStyle stageStyle, ImageLoader icon, boolean captureInput) {
- this.width = RenJava.CONFIGURATION.getWidth();
- this.height = RenJava.CONFIGURATION.getHeight();
- this.title = title;
- this.backgroundColor = backgroundColor;
- this.stageStyle = stageStyle;
- this.icon = icon;
- this.captureInput = captureInput;
- buildStage();
- }
-
- public Window(String title, Color backgroundColor, StageStyle stageStyle, ImageLoader icon, int width, int height) {
- this.title = title;
- this.backgroundColor = backgroundColor;
- this.stageStyle = stageStyle;
- this.icon = icon;
- this.width = width;
- this.height = height;
- buildStage();
- }
-
- public Window(String title, Color backgroundColor, StageStyle stageStyle, ImageLoader icon, int width, int height, boolean captureInput) {
- this.title = title;
- this.backgroundColor = backgroundColor;
- this.stageStyle = stageStyle;
- this.icon = icon;
- this.width = width;
- this.height = height;
- this.captureInput = captureInput;
- buildStage();
- }
-
- public Window(String title, Color backgroundColor, StageStyle stageStyle, ImageLoader icon, int width, int height, boolean captureInput, boolean focused) {
- this.title = title;
- this.backgroundColor = backgroundColor;
- this.stageStyle = stageStyle;
- this.icon = icon;
- this.width = width;
- this.height = height;
- this.captureInput = captureInput;
- this.focused = focused;
+ /**
+ * Constructs a Window instance using properties defined in a {@link WindowBuilder}.
+ * This allows for a flexible and readable way to configure window properties.
+ *
+ * Common styles include {@link StageStyle#DECORATED}, which provides standard
+ * window controls, and {@link StageStyle#UNDECORATED} for a borderless window.
+ *
+ * Example usage:
+ * {@code
+ * WindowBuilder builder = new WindowBuilder()
+ * .setStageStyle(StageStyle.UNDECORATED)
+ * .setDimensions(800, 600)
+ * .setBackgroundColor(Color.BLACK)
+ * Window window = new Window(builder);
+ *
+ * // Add containers
+ * window.addContainer(someContainer);
+ * }
+ * @param builder The {@link WindowBuilder} instance containing window configuration.
+ */
+ public Window(WindowBuilder builder) {
+ this.title = builder.getTitle();
+ this.stageStyle = builder.getStageStyle();
+ this.root = builder.getRoot();
+ this.icon = builder.getIcon();
+ this.width = builder.getWidth();
+ this.height = builder.getHeight();
+ this.initialWidth = builder.getWidth();
+ this.initialHeight = builder.getHeight();
+ this.backgroundColor = builder.getBackgroundColor();
+ this.fullscreen = builder.isFullscreen();
+ this.maximized = builder.isMaximized();
+ this.focused = builder.isFocused();
+ this.scale = builder.isScale();
buildStage();
- }
- public Window(String title, StageStyle stageStyle, ImageLoader icon, int width, int height, boolean captureInput, boolean focused) {
- this.title = title;
- this.stageStyle = stageStyle;
- this.icon = icon;
- this.width = width;
- this.height = height;
- this.captureInput = captureInput;
- this.focused = focused;
- buildStage();
+ // Display stage.
+ render();
}
+ /**
+ * Initializes the JavaFX Stage with the configured properties from the `WindowBuilder`.
+ * This method sets up the title, style, dimensions, icon, and initial scene.
+ */
protected void buildStage() {
stage = new Stage();
if (icon != null) {
- Image windowIcon = null;
- try {
- windowIcon = icon.buildRaw();
- } catch (ImageNotFoundException e) {
- RenLogger.LOGGER.error(e.getMessage(), e);
- RenJava.writeStackTrace(e);
- }
+ Image windowIcon = icon.build();
if (windowIcon != null) {
stage.getIcons().add(windowIcon);
}
@@ -266,50 +147,102 @@ protected void buildStage() {
stage.setMaximized(maximized);
stage.setFullScreen(fullscreen);
+ root.setPrefSize(width, height);
- root = new BorderPane();
+ if (scale) {
+ Scale scale = new Scale(getWidthScale(), getHeightScale(), 0, 0);
+ root.getTransforms().setAll(scale);
+ }
- root.setBackground(new Background(new BackgroundFill(backgroundColor, CornerRadii.EMPTY, Insets.EMPTY)));
+ handleWindowScaling(stage);
scene = new Scene(root);
- scene.setFill(Color.BLACK);
-
stage.setScene(scene);
- if (captureInput) {
- handleStageInput(stage);
- }
}
+ /**
+ * Updates the background color of the window's root pane and scene.
+ * @param color The new background color.
+ */
public void updateBackground(Color color) {
this.backgroundColor = color;
root.setBackground(new Background(new BackgroundFill(color, CornerRadii.EMPTY, Insets.EMPTY)));
stage.getScene().setFill(color);
}
+ /**
+ * Retrieves the current background color of the window.
+ * @return The current background color.
+ */
public Color getBackgroundColor() {
return backgroundColor;
}
+ /**
+ * Retrieves the JavaFX Stage associated with this window.
+ * @return The current Stage.
+ */
public Stage getStage() {
return stage;
}
+ /**
+ * Retrieves the JavaFX Scene associated with this window.
+ * @return The current Scene.
+ */
public Scene getScene() {
return scene;
}
-
+ /**
+ * Retrieves the root Pane of the window's scene graph.
+ * @return The root Pane.
+ */
public Pane getRoot() {
return root;
}
- public boolean hasCaptureInput() {
- return captureInput;
+ /**
+ * Retrieves the configured width of the window.
+ * @return The window width.
+ */
+ public double getWidth() {
+ return width;
+ }
+
+ public void setWidth(double width) {
+ this.width = width;
+ this.initialWidth = width;
+ stage.setWidth(width);
+ root.getTransforms().clear();
+ }
+
+ public void setHeight(double height) {
+ this.height = height;
+ this.initialHeight = height;
+ stage.setHeight(height);
+ root.getTransforms().clear();
}
/**
- * Toggles the stage to full-screen or windowed.
- * @param fullscreen Pass true for fullscreen, false for windowed.
+ * Retrieves the configured height of the window.
+ * @return The window height.
+ */
+ public double getHeight() {
+ return height;
+ }
+
+ public double getWidthScale() {
+ return width / initialWidth;
+ }
+
+ public double getHeightScale() {
+ return height / initialHeight;
+ }
+
+ /**
+ * Toggles the window between full-screen and windowed modes.
+ * @param fullscreen True to set to full-screen, false for windowed.
*/
public void setFullscreen(boolean fullscreen) {
this.fullscreen = fullscreen;
@@ -322,6 +255,10 @@ public void setFullscreen(boolean fullscreen) {
}
}
+ /**
+ * Toggles the window between maximized and normal states.
+ * @param maximized True to maximize the window, false for normal size.
+ */
public void setMaximized(boolean maximized) {
this.maximized = maximized;
if (stage != null) {
@@ -333,52 +270,181 @@ public void setMaximized(boolean maximized) {
}
}
+ /**
+ * Adds a {@link Container} to the window using its intrinsic index.
+ * @param container The container to add.
+ */
public void addContainer(Container container) {
- this.containers.add(container);
+ addContainer(container, container.getIndex());
}
- public void addContainers(Container... containers) {
- this.containers.addAll(List.of(containers));
+ public void addContainer(Container container, Node node) {
+ addContainer(container, node, container.getIndex());
}
- public void addContainers(LinkedList cont) {
- this.containers.addAll(cont);
+
+ /**
+ * Adds a {@link Container} to the window at a specific index. If a container already exists at the given index,
+ * it attempts to shift existing containers to accommodate the new one.
+ * @param container The container to add.
+ * @param index The desired rendering index for the container.
+ */
+ public void addContainer(Container container, int index) {
+ Container current = containers.get(index);
+ if (current != null) {
+ int i = index + 1;
+ removeContainer(current);
+ addContainer(current, i);
+ }
+ containers.put(index, container);
+
+ Node assemble = container.assemble();
+
+ if (index > 0) {
+ if (root.getChildren().size() < index) {
+ root.getChildren().addLast(assemble);
+ } else {
+ root.getChildren().add(index, assemble);
+ }
+ } else {
+ root.getChildren().add(assemble);
+ }
}
- public void setContainers(LinkedList containers) {
+ /**
+ * Adds a pre-compiled {@link Container} to the window. Use {@link Container#assemble()} to build the {@link Node}.
+ * @param container The container to add.
+ * @param node The pre-compiled node to add.
+ * @param index The desired rendering index for the container.
+ */
+ public void addContainer(Container container, Node node, int index) {
+ Container current = containers.get(index);
+ if (current != null) {
+ int i = index + 1;
+ removeContainer(current);
+ addContainer(current, i);
+ }
+ containers.put(index, container);
+ root.getChildren().add(node);
+ }
+
+ /**
+ * Adds all containers from the given TreeMap to this window's container collection.
+ * Existing containers with matching indices will be overwritten.
+ * @param con The TreeMap of containers to add.
+ */
+ public void addContainers(TreeMap con) {
+ this.containers.putAll(con);
+ }
+
+ /**
+ * Replaces the entire set of containers in the window with a new TreeMap of containers.
+ * @param containers The new TreeMap of containers.
+ */
+ public void setContainers(TreeMap containers) {
this.containers = containers;
}
+ /**
+ * Replaces an old container instance with a new container instance, preserving its original index.
+ * The old container must already exist in the window's collection.
+ * @param oldContainer The container instance to be replaced.
+ * @param newContainer The new container instance to take its place.
+ */
+ public void replaceContainer(Container oldContainer, Container newContainer) {
+ if (containers.containsValue(oldContainer)) {
+ containers.replace(oldContainer.getIndex(), newContainer);
+ }
+ }
+
+ /**
+ * Replaces the container at a specific index with a new container.
+ * The window is then re-rendered to reflect this change.
+ * @param index The index at which to replace the container.
+ * @param container The new container to place at the specified index.
+ */
+ public void replaceContainer(int index, Container container) {
+ containers.remove(index);
+ containers.replace(index, container);
+ render();
+ }
+
+ /**
+ * Removes a specific {@link Container} instance from the window's collection.
+ * Note: This only removes the container from the internal map,
+ * it does not automatically remove its corresponding JavaFX Node from the scene graph.
+ * A subsequent `render()` call would be needed to update the display.
+ * @param container The container instance to remove.
+ */
+ public void removeContainer(Container container) {
+ int toRemove = -1;
+ for (Map.Entry entry : containers.entrySet()) {
+ if (entry.getValue() == container) {
+ toRemove = entry.getKey();
+ root.getChildren().remove(container.getNode());
+ break;
+ }
+ }
+ containers.remove(toRemove);
+
+ if (currentPopup == container) {
+ currentPopup = null;
+ }
+ }
+
+ /**
+ * Clears all containers from the window.
+ * A garbage collection hint is provided to the JVM.
+ */
public void clearContainers() {
+ new LinkedList<>(containers.values()).forEach(this::removeContainer);
containers.clear();
- System.gc();
}
+ /**
+ * Removes the container at a specific index from the window and re-renders the display.
+ * @param index The index of the container to remove.
+ */
public void clearContainer(int index) {
containers.remove(index);
-
- // Re-render
render();
}
- public LinkedList getContainers() {
+ /**
+ * Retrieves the TreeMap of all containers currently managed by the window.
+ * @return A TreeMap mapping container indices to Container objects.
+ */
+ public TreeMap getContainers() {
return containers;
}
+ /**
+ * Clears all child nodes from the root pane and re-sets the scene's root.
+ * The stage is then shown.
+ */
public void clean() {
root.getChildren().clear();
scene.setRoot(root);
stage.show();
}
- // Clears and resets current window.
+ /**
+ * Clears all containers and resets the window's root pane and scene.
+ * The stage is not automatically shown after this operation.
+ */
public void clear() {
clear(false);
}
+ /**
+ * Clears all containers and resets the window's root pane and scene.
+ * Optionally shows the stage after clearing.
+ * @param render True to show the stage after clearing, false otherwise.
+ */
public void clear(boolean render) {
clearContainers();
this.root = new Pane();
+ root.setPrefSize(width, height);
this.scene = new Scene(root);
this.stage.setScene(scene);
if (render) {
@@ -386,21 +452,46 @@ public void clear(boolean render) {
}
}
+ /**
+ * Closes the JavaFX Stage associated with this window.
+ * A garbage collection hint is provided to the JVM.
+ */
public void close() {
if (stage != null) {
stage.close();
- System.gc(); // Force garbage collection once the window is closed.
}
}
+ /**
+ * Clears the root pane's children, creates a new Stage, and hides it.
+ * This method essentially resets the visual state of the window without closing it.
+ */
+ public void resetStage() {
+ buildStage();
+ }
+
+ /**
+ * Shows the window's stage.
+ * Note: This method is named "hide" but performs "show". This might be a naming inconsistency.
+ */
+ public void hide() {
+ stage.show();
+ }
+
+ /**
+ * Builds the JavaFX Stage and then renders all active nodes on the screen.
+ */
public void buildAndRender() {
buildStage();
render();
}
/**
- * Builds and displays all active nodes on the screen. Can cause flicker if called excessively. If you changed by adding, modifying, or removing {@link Overlay} or {@link Container} you must call this function.
- * This function translates RenJava API into JavaFX and updates the stage and scene.
+ * Builds and displays all active nodes on the screen. This function translates the engine's API into JavaFX and updates the stage and scene.
+ *
+ * Calling this excessively can cause visual flicker. It must be called after adding,
+ * modifying, or removing {@link Overlay} or {@link Container} to update the display.
+ *
*/
public void render() {
build();
@@ -408,188 +499,105 @@ public void render() {
stage.requestFocus();
}
stage.show();
- // Force clear resources that are unused.
- // To those who feel like GC is bad practice or indicates broken code allow me to explain.
- // Garbage is automatically collected and deleted by the JVM which is good enough for most cases.
- // HOWEVER, when you are rendering and loading multiple 10mb+ images within a 5 minute time period auto GC is far too slow.
- // This call may not do anything at all at times. It tells the JVM that I want to clear any unused references pronto not when it wants to.
- // There are multiple gc calls within the framework and when testing on my own machine they dramatically decrease resource usage by 300mb+
- // I will admit that there may be in a memory leak somewhere in the framework, but this is not the solution to that.
- //
- // TL;DR I ain't waiting for your slow ass jvm to clear resources.
- System.gc();
-
}
/**
- * This function is used to build the RenJava API onto the JavaFX framework. This will not render the built nodes onto the screen. Recommended to use {@link #render()} for most use cases.
+ * Builds the engine's API onto the JavaFX framework without displaying the built nodes on the screen.
+ * For most use cases, {@link #render()} is recommended as it also shows the updated display.
*/
public void build() {
- build(false);
+ if (!containers.isEmpty()) {
+ build(false);
+ }
}
+ /**
+ * Builds the engine's API onto the JavaFX framework, optionally resetting the scene.
+ * This method processes and prepares containers for display but does not automatically show them.
+ * @param reset True to reset the scene (clears and initialises the root pane and scene), false to only clear children.
+ */
public void build(boolean reset) {
- if (containers.isEmpty()) {
- RenLogger.LOGGER.error("You must add containers to the window before every render call.");
- }
-
root.getChildren().clear();
+ root.getStylesheets().clear();
if (reset) {
- // Causes flickering but needed when capturing scene.
- // Resets the scene.
+ // Resets the scene. This can cause flickering but might be needed in specific capture scenarios.
this.root = new Pane();
+ root.setPrefSize(width, height);
this.scene = new Scene(root);
this.stage.setScene(scene);
}
- // Gather orders
- LinkedList lowOrder = new LinkedList<>();
- LinkedList normalOrder = new LinkedList<>();
- LinkedList highOrder = new LinkedList<>();
-
- for (Container container : containers) {
- if (container.getOrder() == DisplayOrder.LOW) {
- lowOrder.add(container);
- } else if (container.getOrder() == DisplayOrder.NORMAL) {
- normalOrder.add(container);
- } else if (container.getOrder() == DisplayOrder.HIGH) {
- highOrder.add(container);
- }
- }
-
-
- lowOrder.forEach(this::renderContainer);
- normalOrder.forEach(this::renderContainer);
- highOrder.forEach(this::renderContainer);
-
- // Not sure if this will cause issues but to reduce resource usage the mappings need to be cleared
- lowOrder.clear();
- normalOrder.clear();
- highOrder.clear();
+ containers.values().forEach(this::renderContainer);
}
- // Renders container on top of current window
+ /**
+ * Renders a specific container by adding it on top of the current window's content.
+ * The container is automatically assigned an index that places it at the highest layer.
+ * @param container The container to render.
+ */
public void render(Container container) {
- containers.add(container);
+ int index = containers.isEmpty() ? 1 : containers.lastKey() + 1;
+ container.setIndex(index);
+ containers.put(index, container);
renderContainer(container);
}
+ /**
+ * Renders a single {@link Container} instance by building its corresponding JavaFX Node
+ * and adding it to the window's root pane.
+ * @param container The container to render.
+ */
private void renderContainer(Container container) {
- Map.Entry> entry = container.build();
- Node node = entry.getKey();
-
- node.prefHeight(container.getHeight());
- node.prefWidth(container.getWidth());
- node.setTranslateX(container.getX());
- node.setTranslateY(container.getY());
-
- for (Node n : entry.getValue()) {
- if (node instanceof Pane pane) {
- pane.getChildren().add(n);
- }
- // Different pane types
+ if (container.getNode() != null) {
+ root.getChildren().remove(container.getNode());
}
- getRoot().getChildren().add(node);
-
- ContainerRenderEvent renderEvent = new ContainerRenderEvent(container, node);
- RenJava.getEventHandler().callEvent(renderEvent);
+ root.getChildren().add(container.assemble());
}
- private void handleStageInput(Stage stage) {
- stage.addEventFilter(ScrollEvent.SCROLL, event -> {
- double y = event.getDeltaY();
- if (y > 0) {
- // Scroll up
- ScrollUpEvent scrollUpEvent = new ScrollUpEvent();
- RenJava.getEventHandler().callEvent(scrollUpEvent);
- } else {
- ScrollDownEvent downEvent = new ScrollDownEvent();
- RenJava.getEventHandler().callEvent(downEvent);
- }
- });
- stage.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
- if (Arrays.stream(ModifierKeyList.modifier).anyMatch(keyCode -> keyCode == event.getCode())) {
- KeyCode keyCode = KeyUtils.getCurrentKeyDown();
- if (keyCode != event.getCode()) {
- KeyUtils.setModifierDown(keyCode, false);
- }
-
- if (keyCode == null) {
- // Engine says key was not held before but it is now.
- // Update engine
- KeyUtils.setModifierDown(event.getCode(), true); // So far it is down.
-
- // Start Sub-thread for continuous event
- Tasks.runAsync(() -> {
- firstRun = Instant.now();
- while (KeyUtils.getCurrentKeyDown() != null) {
- // Add delay threshold
- Instant current = Instant.now();
- if (lastRun == null) {
- Tasks.runJavaFXThread(() -> {
- KeyPressEvent event1 = new KeyPressEvent(event); // Might not pass
- RenJava.getEventHandler().callEvent(event1);
- });
- lastRun = current;
- } else {
- long diff = Duration.between(lastRun, current).toMillis();
- long firstDiff = Duration.between(firstRun, current).toMinutes();
- if (firstDiff > 20) { // This broke randomly???
- RenLogger.LOGGER.warn("Modifier key was held for 2 minutes. Killing task...");
- KeyUtils.setModifierDown(event.getCode(), false);
- return; // Kill after 2min
- }
- if (diff > 75) {
- Tasks.runJavaFXThread(() -> {
- KeyPressEvent event1 = new KeyPressEvent(event); // Might not pass
- RenJava.getEventHandler().callEvent(event1);
- });
- lastRun = current;
- }
- }
- }
- });
- }
- } else {
- KeyPressEvent pressEvent = new KeyPressEvent(event);
- RenJava.getEventHandler().callEvent(pressEvent);
- }
- });
-
+ /**
+ * Renders a popup container, ensuring that only one popup is active at a time.
+ * If a previous popup exists, its Node is removed from the scene graph before the new one is added.
+ * This method does not apply translation (X, Y) from the container's properties directly to the node,
+ * assuming these are handled by the calling `renderPopup` method.
+ * @param container The container to render as a popup.
+ */
+ private void renderPopupContainer(Container container) {
+ if (currentPopup != null) {
+ removeContainer(currentPopup);
+ }
+ currentPopup = container;
- stage.addEventFilter(KeyEvent.KEY_RELEASED,event -> {
+ addContainer(container);
+ }
- if (Arrays.stream(ModifierKeyList.modifier).anyMatch(keyCode -> keyCode == event.getCode())) {
- // If CTRL is being set to down set to false to stop the while thread
- if (KeyUtils.getCurrentKeyDown() != null) {
- KeyUtils.setModifierDown(event.getCode(), false);
- KeyReleaseEvent releaseEvent = new KeyReleaseEvent(event);
- RenJava.getEventHandler().callEvent(releaseEvent);
- }
- } else {
- KeyReleaseEvent releaseEvent = new KeyReleaseEvent(event);
- RenJava.getEventHandler().callEvent(releaseEvent);
- }
- });
+ public Container getCurrentPopup() {
+ return currentPopup;
+ }
+ private void handleWindowScaling(Stage stage) {
+ // This scales the application to the desired width and height that it is running at.
stage.heightProperty().addListener((observable, oldValue, newValue) -> {
- RenJava.CONFIGURATION.setCurrentWindowHeight(newValue.doubleValue());
+ this.height = newValue.doubleValue();
- double scaleWidth = RenJava.CONFIGURATION.getWidthScale();
- double scaleHeight = newValue.doubleValue() / RenJava.CONFIGURATION.getHeight();
+ double scaleWidth = getWidthScale();
+ double scaleHeight = getHeightScale();
- Scale scale = new Scale(scaleWidth, scaleHeight, 0, 0);
- root.getTransforms().setAll(scale);
+ if (scale) {
+ Scale scale = new Scale(scaleWidth, scaleHeight, 0, 0);
+ root.getTransforms().setAll(scale);
+ }
});
stage.widthProperty().addListener((observable, oldValue, newValue) -> {
- RenJava.CONFIGURATION.setCurrentWindowWidth(newValue.doubleValue());
+ this.width = newValue.doubleValue();
- double scaleWidth = newValue.doubleValue() / RenJava.CONFIGURATION.getWidth();
- double scaleHeight = RenJava.CONFIGURATION.getHeightScale();
+ double scaleWidth = getWidthScale();
+ double scaleHeight = getHeightScale();
- Scale scale = new Scale(scaleWidth, scaleHeight, 0, 0);
- root.getTransforms().setAll(scale);
+ if (scale) {
+ Scale scale = new Scale(scaleWidth, scaleHeight, 0, 0);
+ root.getTransforms().setAll(scale);
+ }
});
}
@@ -597,5 +605,4 @@ public void handleSceneTransition(RenScene scene, Transitions transitions) {
// Play transition on the current root
transitions.play(scene);
}
-
}
\ No newline at end of file
diff --git a/src/main/java/me/piitex/renjava/gui/WindowBuilder.java b/src/main/java/me/piitex/renjava/gui/WindowBuilder.java
new file mode 100644
index 00000000..b12212c7
--- /dev/null
+++ b/src/main/java/me/piitex/renjava/gui/WindowBuilder.java
@@ -0,0 +1,185 @@
+package me.piitex.renjava.gui;
+
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.Color;
+import javafx.stage.StageStyle;
+import me.piitex.renjava.api.loaders.ImageLoader;
+
+/**
+ * A builder class for constructing Window objects with various configuration options.
+ * This provides a more intuitive and flexible way to create Window instances
+ * compared to using multiple constructors.
+ *
+ * {@code
+ * Window window = new WindowBuild("My Game")
+ * .setStageStyle(StageStyle.UNDECORATED)
+ * setRoot(new Pane())
+ * .setDimensions(1280, 720)
+ * .setBackgroundColor(Color.DARKBLUE)
+ * .setFullscreen(true)
+ * .build();
+ * }
+ *
+ */
+public class WindowBuilder {
+ private final String title;
+ private StageStyle stageStyle = StageStyle.DECORATED;
+ private Pane root = new Pane();
+ private ImageLoader icon;
+ private double width = 1920;
+ private double height = 1080;
+ private Color backgroundColor = Color.BLACK;
+ private boolean fullscreen = false;
+ private boolean maximized = false;
+ private boolean focused = true;
+ private boolean scale = true;
+
+ /**
+ * Starts the building process for a new Window with a required title.
+ *
+ * @param title The title of the window.
+ */
+ public WindowBuilder(String title) {
+ this.title = title;
+ }
+
+ /**
+ * Sets the style of the window.
+ * @param stageStyle The {@link StageStyle} for the window (e.g., DECORATED, UNDECORATED).
+ * @return The current WindowBuild instance for chaining.
+ */
+ public WindowBuilder setStageStyle(StageStyle stageStyle) {
+ this.stageStyle = stageStyle;
+ return this;
+ }
+
+ /**
+ * Sets the scene root pane.
+ * @param root The pane type for the root.
+ * @return The current WindowBuild instance for chaining.
+ */
+ public WindowBuilder setRoot(Pane root) {
+ this.root = root;
+ return this;
+ }
+
+ /**
+ * Sets the icon for the window.
+ * @param icon An {@link ImageLoader} for the window's icon.
+ * @return The current WindowBuild instance for chaining.
+ */
+ public WindowBuilder setIcon(ImageLoader icon) {
+ this.icon = icon;
+ return this;
+ }
+
+ /**
+ * Sets the preferred width and height of the window.
+ * @param width The width of the window.
+ * @param height The height of the window.
+ * @return The current WindowBuild instance for chaining.
+ */
+ public WindowBuilder setDimensions(double width, double height) {
+ this.width = width;
+ this.height = height;
+ return this;
+ }
+
+ /**
+ * Sets the background color of the window's root pane.
+ * @param backgroundColor The {@link Color} for the window's background.
+ * @return The current WindowBuild instance for chaining.
+ */
+ public WindowBuilder setBackgroundColor(Color backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ return this;
+ }
+
+ /**
+ * Sets whether the window should be in fullscreen mode.
+ * @param fullscreen True for fullscreen, false otherwise.
+ * @return The current WindowBuild instance for chaining.
+ */
+ public WindowBuilder setFullscreen(boolean fullscreen) {
+ this.fullscreen = fullscreen;
+ return this;
+ }
+
+ /**
+ * Sets whether the window should be maximized on launch.
+ * @param maximized True to maximize, false otherwise.
+ * @return The current WindowBuild instance for chaining.
+ */
+ public WindowBuilder setMaximized(boolean maximized) {
+ this.maximized = maximized;
+ return this;
+ }
+
+ /**
+ * Sets whether the window should be focused on launch.
+ * @param focused True to focus, false otherwise.
+ * @return The current WindowBuild instance for chaining.
+ */
+ public WindowBuilder setFocused(boolean focused) {
+ this.focused = focused;
+ return this;
+ }
+
+ public WindowBuilder setScale(boolean scale) {
+ this.scale = scale;
+ return this;
+ }
+
+ /**
+ * Constructs and returns a new {@link Window} object based on the builder's configurations.
+ * @return A new Window instance.
+ */
+ public Window build() {
+ return new Window(this); // Calls the private constructor in the Window class
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public StageStyle getStageStyle() {
+ return stageStyle;
+ }
+
+ public Pane getRoot() {
+ return root;
+ }
+
+ public ImageLoader getIcon() {
+ return icon;
+ }
+
+ public double getWidth() {
+ return width;
+ }
+
+ public double getHeight() {
+ return height;
+ }
+
+ public Color getBackgroundColor() {
+ return backgroundColor;
+ }
+
+ public boolean isFullscreen() {
+ return fullscreen;
+ }
+
+ public boolean isMaximized() {
+ return maximized;
+ }
+
+ public boolean isFocused() {
+ return focused;
+ }
+
+ public boolean isScale() {
+ return scale;
+ }
+
+}
diff --git a/src/main/java/me/piitex/renjava/gui/containers/EmptyContainer.java b/src/main/java/me/piitex/renjava/gui/containers/EmptyContainer.java
index 368bf7ba..cd60d054 100644
--- a/src/main/java/me/piitex/renjava/gui/containers/EmptyContainer.java
+++ b/src/main/java/me/piitex/renjava/gui/containers/EmptyContainer.java
@@ -4,12 +4,9 @@
import javafx.scene.layout.Pane;
import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.DisplayOrder;
import me.piitex.renjava.gui.Window;
import me.piitex.renjava.gui.layouts.Layout;
-import me.piitex.renjava.gui.overlays.ImageOverlay;
import me.piitex.renjava.gui.overlays.Overlay;
-import me.piitex.renjava.loggers.RenLogger;
import java.util.AbstractMap;
import java.util.LinkedList;
@@ -20,9 +17,8 @@
* The container must be added to a {@link Window} to be rendered.
*
* {@code
- * EmptyContainer container = new EmptyContainer(x, y, width, height, displayOrder);
+ * EmptyContainer container = new EmptyContainer(x, y, width, height, index);
* window.addContainer(container);
- * window.render();
* }
*
* @see Container
@@ -31,37 +27,56 @@
* @see Window
*/
public class EmptyContainer extends Container {
+ private final Pane pane;
- public EmptyContainer(double x, double y, double width, double height) {
- super(x, y, width, height);
+ public EmptyContainer(double x, double y, double width, double height, int index) {
+ super(new Pane(), x, y, width, height, index);
+ this.pane = (Pane) getNode();
}
- public EmptyContainer(double x, double y, double width, double height, DisplayOrder order) {
- super(x, y, width, height, order);
+ public EmptyContainer(double x, double y, double width, double height) {
+ this(x, y, width, height, 0);
}
public EmptyContainer(double width, double height) {
- super(0, 0, width, height);
+ this(0, 0, width, height, 0);
}
- public EmptyContainer(double width, double height, DisplayOrder order) {
- super(0, 0, width, height, order);
+ public EmptyContainer(double width, double height, int index) {
+ this(0, 0, width, height, index);
+ }
+
+ public Pane getPane() {
+ return pane;
}
@Override
- public Map.Entry> build() {
- Pane pane = new Pane();
+ public Node build() {
pane.setTranslateX(getX());
pane.setTranslateY(getY());
- pane.setPrefSize(getWidth(), getHeight());
+ if (getWidth() > 0) {
+ pane.setMinWidth(getWidth());
+ }
+ if (getHeight() > 0) {
+ pane.setMinHeight(getHeight());
+ }
+
+ if (getPrefWidth() > 0) {
+ pane.setPrefWidth(getPrefWidth());
+ }
+ if (getPrefHeight() > 0) {
+ pane.setPrefHeight(getPrefHeight());
+ }
- LinkedList lowOrder = new LinkedList<>();
- LinkedList normalOrder = new LinkedList<>();
- LinkedList highOrder = new LinkedList<>();
+ if (getMaxWidth() > 0) {
+ pane.setMaxWidth(getMaxWidth());
+ }
+ if (getMaxHeight() > 0) {
+ pane.setMaxHeight(getMaxHeight());
+ }
- buildBase(lowOrder, normalOrder, highOrder);
+ setStyling(pane);
- // Return loworder because the other orders are added onto the low order.
- return new AbstractMap.SimpleEntry<>(pane, lowOrder);
+ return pane;
}
}
diff --git a/src/main/java/me/piitex/renjava/gui/containers/LayoutContainer.java b/src/main/java/me/piitex/renjava/gui/containers/LayoutContainer.java
new file mode 100644
index 00000000..072f3c55
--- /dev/null
+++ b/src/main/java/me/piitex/renjava/gui/containers/LayoutContainer.java
@@ -0,0 +1,130 @@
+package me.piitex.renjava.gui.containers;
+
+import javafx.scene.Node;
+import javafx.scene.layout.BorderPane;
+import me.piitex.renjava.gui.Container;
+import me.piitex.renjava.gui.Element;
+
+public class LayoutContainer extends Container {
+ private final BorderPane pane;
+
+ private Element top, bottom, right, left, center, border;
+
+ public LayoutContainer(double x, double y, double width, double height, int index) {
+ super(new BorderPane(), x, y, width, height, index);
+ this.pane = (BorderPane) getNode();
+ }
+
+ public LayoutContainer(double x, double y, double width, double height) {
+ this(x, y, width, height, 0);
+ }
+
+ public LayoutContainer(double width, double height) {
+ this(0, 0, width, height, 0);
+ }
+
+ public LayoutContainer(double width, double height, int index) {
+ this(0, 0, width, height, index);
+ }
+
+ public BorderPane getPane() {
+ return pane;
+ }
+
+ public Element getTop() {
+ return top;
+ }
+
+ public void setTop(Element top) {
+ this.top = top;
+ }
+
+ public Element getBottom() {
+ return bottom;
+ }
+
+ public void setBottom(Element bottom) {
+ this.bottom = bottom;
+ }
+
+ public Element getRight() {
+ return right;
+ }
+
+ public void setRight(Element right) {
+ this.right = right;
+ }
+
+ public Element getLeft() {
+ return left;
+ }
+
+ public void setLeft(Element left) {
+ this.left = left;
+ }
+
+ public Element getCenter() {
+ return center;
+ }
+
+ public void setCenter(Element center) {
+ this.center = center;
+ }
+
+ public Element getBorder() {
+ return border;
+ }
+
+ public void setBorder(Element border) {
+ this.border = border;
+ }
+
+ @Override
+ public Node build() {
+ pane.setTranslateX(getX());
+ pane.setTranslateY(getY());
+ if (getWidth() > 0) {
+ pane.setMinWidth(getWidth());
+ }
+ if (getHeight() > 0) {
+ pane.setMinHeight(getHeight());
+ }
+
+ if (getPrefWidth() > 0) {
+ pane.setPrefWidth(getPrefWidth());
+ }
+ if (getPrefHeight() > 0) {
+ pane.setPrefHeight(getPrefHeight());
+ }
+
+ if (getMaxWidth() > 0) {
+ pane.setMaxWidth(getMaxWidth());
+ }
+ if (getMaxHeight() > 0) {
+ pane.setMaxHeight(getMaxHeight());
+ }
+
+ if (top != null) {
+ pane.setTop(top.assemble());
+ }
+ if (bottom != null) {
+ pane.setBottom(bottom.assemble());
+ }
+ if (right != null) {
+ pane.setRight(right.assemble());
+ }
+ if (left != null) {
+ pane.setLeft(left.assemble());
+ }
+ if (center != null) {
+ pane.setCenter(center.assemble());
+ }
+ if (border != null) {
+ pane.setBottom(border.assemble());
+ }
+
+ setStyling(pane);
+
+ return pane;
+ }
+}
diff --git a/src/main/java/me/piitex/renjava/gui/containers/ScrollContainer.java b/src/main/java/me/piitex/renjava/gui/containers/ScrollContainer.java
index 5bfaf55f..73517560 100644
--- a/src/main/java/me/piitex/renjava/gui/containers/ScrollContainer.java
+++ b/src/main/java/me/piitex/renjava/gui/containers/ScrollContainer.java
@@ -2,21 +2,24 @@
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
-import javafx.scene.layout.Pane;
+import javafx.scene.layout.VBox;
import me.piitex.renjava.gui.Container;
import me.piitex.renjava.gui.layouts.Layout;
-import java.util.AbstractMap;
-import java.util.LinkedList;
-import java.util.Map;
-
public class ScrollContainer extends Container {
+ private final ScrollPane scrollPane;
private final Layout layout;
- private ScrollPane scrollPane;
private double xOffset, yOffset;
+ private boolean horizontalScroll = true;
+ private boolean verticalScroll = true;
+ private boolean scrollWhenNeeded = true;
+ private boolean scrollToBottom = false;
+ private boolean pannable = false;
+ private double scrollPosition;
public ScrollContainer(Layout layout, double x, double y, double width, double height) {
- super(x, y, width, height);
+ super(new ScrollPane(), x, y, width, height);
+ this.scrollPane = (ScrollPane) getNode();
this.layout = layout;
}
@@ -36,35 +39,106 @@ public void setYOffset(double yOffset) {
this.yOffset = yOffset;
}
+ public boolean isHorizontalScroll() {
+ return horizontalScroll;
+ }
+
+ public void setHorizontalScroll(boolean horizontalScroll) {
+ this.horizontalScroll = horizontalScroll;
+ }
+
+ public boolean isVerticalScroll() {
+ return verticalScroll;
+ }
+
+ public void setVerticalScroll(boolean verticalScroll) {
+ this.verticalScroll = verticalScroll;
+ }
+
+ public boolean isScrollWhenNeeded() {
+ return scrollWhenNeeded;
+ }
+
+ public void setScrollWhenNeeded(boolean scrollWhenNeeded) {
+ this.scrollWhenNeeded = scrollWhenNeeded;
+ }
+
+ public void setScrollPosition(double scrollPosition) {
+ this.scrollPosition = scrollPosition;
+ }
+
+ public void setScrollToBottom(boolean scrollToBottom) {
+ this.scrollToBottom = scrollToBottom;
+ }
+
+ public void setPannable(boolean pannable) {
+ this.pannable = pannable;
+ }
+
+ public ScrollPane getScrollPane() {
+ return scrollPane;
+ }
+
+ public Layout getLayout() {
+ return layout;
+ }
+
@Override
- public Map.Entry> build() {
- scrollPane = new ScrollPane();
+ public Node build() {
+ scrollPane.setVvalue(scrollPosition);
scrollPane.setTranslateX(getX());
scrollPane.setTranslateY(getY());
- scrollPane.setPrefSize(getWidth(), getHeight());
- scrollPane.vbarPolicyProperty().setValue(ScrollPane.ScrollBarPolicy.ALWAYS);
-
- // Build pane layout for the scroll content
- Pane pane = layout.getPane();
- scrollPane.setContent(pane);
+ scrollPane.setPannable(pannable);
+ if (getWidth() > 0) {
+ scrollPane.setMinWidth(getWidth());
+ }
+ if (getHeight() > 0) {
+ scrollPane.setMinHeight(getHeight());
+ }
+ if (getPrefWidth() > 0) {
+ scrollPane.setPrefWidth(getPrefWidth());
+ }
+ if (getPrefHeight() > 0) {
+ scrollPane.setPrefHeight(getPrefHeight());
+ }
- LinkedList lowOrder = new LinkedList<>();
- LinkedList normalOrder = new LinkedList<>();
- LinkedList highOrder = new LinkedList<>();
+ if (getMaxWidth() > 0) {
+ scrollPane.setMaxWidth(getMaxWidth());
+ }
+ if (getMaxHeight() > 0) {
+ scrollPane.setMaxHeight(getMaxHeight());
+ }
+ if (verticalScroll) {
+ scrollPane.vbarPolicyProperty().setValue(ScrollPane.ScrollBarPolicy.ALWAYS);
+ } else if (scrollWhenNeeded) {
+ scrollPane.vbarPolicyProperty().setValue(ScrollPane.ScrollBarPolicy.AS_NEEDED);
+ } else {
+ scrollPane.vbarPolicyProperty().setValue(ScrollPane.ScrollBarPolicy.NEVER);
+ }
+ if (horizontalScroll) {
+ scrollPane.hbarPolicyProperty().setValue(ScrollPane.ScrollBarPolicy.ALWAYS);
+ } else if (scrollWhenNeeded) {
+ scrollPane.hbarPolicyProperty().setValue(ScrollPane.ScrollBarPolicy.AS_NEEDED);
+ } else {
+ scrollPane.hbarPolicyProperty().setValue(ScrollPane.ScrollBarPolicy.NEVER);
+ }
- lowOrder.add(layout.render(this));
+ setStyling(scrollPane);
- buildBase(lowOrder, normalOrder, highOrder);
+ // Build pane layout for the scroll content
+ VBox pane = (VBox) layout.assemble();
+ pane.setAlignment(layout.getAlignment());
- // Offset overlays by 10
- if (xOffset > 0 || yOffset > 0) {
- lowOrder.forEach(node -> {
- node.setTranslateX(node.getTranslateX() + xOffset);
- node.setTranslateY(node.getTranslateX() + yOffset);
+ if (scrollToBottom) {
+ pane.heightProperty().addListener(observable -> {
+ scrollPane.setVvalue(1);
});
}
- return new AbstractMap.SimpleEntry<>(scrollPane, lowOrder);
+ scrollPane.setContent(pane);
+ scrollPane.requestFocus();
+
+ return scrollPane;
}
}
\ No newline at end of file
diff --git a/src/main/java/me/piitex/renjava/gui/layouts/HorizontalLayout.java b/src/main/java/me/piitex/renjava/gui/layouts/HorizontalLayout.java
index 78637294..004704a8 100644
--- a/src/main/java/me/piitex/renjava/gui/layouts/HorizontalLayout.java
+++ b/src/main/java/me/piitex/renjava/gui/layouts/HorizontalLayout.java
@@ -1,16 +1,16 @@
package me.piitex.renjava.gui.layouts;
+import javafx.geometry.Insets;
import javafx.scene.Node;
-import javafx.scene.layout.HBox;
-import javafx.scene.layout.Pane;
-import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.overlays.Overlay;
+import javafx.scene.layout.*;
public class HorizontalLayout extends Layout {
private int spacing;
+ private final HBox pane;
public HorizontalLayout(double width, double height) {
super(new HBox(), width, height);
+ this.pane = (HBox) getPane();
}
public int getSpacing() {
@@ -22,23 +22,42 @@ public void setSpacing(int spacing) {
}
@Override
- public Pane render(Container container) {
+ public Node render() {
// Clear
- HBox pane = (HBox) getPane();
+ VBox.setVgrow(pane, Priority.ALWAYS);
pane.setSpacing(getSpacing());
pane.setTranslateX(getX());
pane.setTranslateY(getY());
pane.getChildren().clear();
+ if (getWidth() > 0) {
+ pane.setMinWidth(getWidth());
+ }
+ if (getHeight() > 0) {
+ pane.setMinHeight(getHeight());
+ }
- for (Overlay overlay : getOverlays()) {
- Node node = overlay.render();
- pane.getChildren().add(node);
+ if (getPrefWidth() > 0) {
+ pane.setPrefWidth(getPrefWidth());
+ }
+ if (getPrefHeight() > 0) {
+ pane.setPrefHeight(getPrefHeight());
}
- for (Layout layout : getChildLayouts()) {
- pane.getChildren().add(layout.render(container));
+ if (getMaxWidth() > 0) {
+ pane.setMaxWidth(getMaxWidth());
+ }
+ if (getMaxHeight() > 0) {
+ pane.setMaxHeight(getMaxHeight());
}
+ if (getAlignment() != null) {
+ pane.setAlignment(getAlignment());
+ }
+ if (getBackgroundColor() != null) {
+ pane.setBackground(new Background(new BackgroundFill(getBackgroundColor(), CornerRadii.EMPTY, Insets.EMPTY)));
+ }
+
- return getPane();
+ setStyling(pane);
+ return pane;
}
}
diff --git a/src/main/java/me/piitex/renjava/gui/layouts/Layout.java b/src/main/java/me/piitex/renjava/gui/layouts/Layout.java
index 1d912636..2bb1f46f 100644
--- a/src/main/java/me/piitex/renjava/gui/layouts/Layout.java
+++ b/src/main/java/me/piitex/renjava/gui/layouts/Layout.java
@@ -1,25 +1,22 @@
package me.piitex.renjava.gui.layouts;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
import javafx.scene.layout.Pane;
-import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.DisplayOrder;
-import me.piitex.renjava.gui.overlays.Overlay;
+import me.piitex.renjava.gui.Renderer;
-import java.util.LinkedList;
-import java.util.List;
-
-public abstract class Layout {
+public abstract class Layout extends Renderer {
private final Pane pane;
private double x, y;
- private final double width, height;
- private DisplayOrder order = DisplayOrder.LOW;
- private final LinkedList overlays = new LinkedList<>();
- private final LinkedList childLayouts = new LinkedList<>();
+ private Insets padding;
+ private Pos alignment;
protected Layout(Pane pane, double width, double height) {
this.pane = pane;
- this.width = width;
- this.height = height;
+ setNode(pane);
+ setWidth(width);
+ setHeight(height);
}
public Pane getPane() {
@@ -42,54 +39,21 @@ public void setY(double y) {
this.y = y;
}
- public DisplayOrder getOrder() {
- return order;
- }
-
- public void setOrder(DisplayOrder order) {
- this.order = order;
-
- // Update order for all overlays
- for (Overlay overlay : getOverlays()) {
- overlay.setOrder(order);
- }
-
- for (Layout layout : getChildLayouts()) {
- layout.setOrder(order);
- }
- }
-
- public LinkedList getOverlays() {
- return overlays;
- }
-
- public void addOverlay(Overlay overlay) {
- this.overlays.add(overlay);
- }
-
- public void addOverlays(Overlay... overlays) {
- this.overlays.addAll(List.of(overlays));
- }
-
- public void addOverlays(List overlays) {
- this.overlays.addAll(overlays);
- }
-
- public LinkedList getChildLayouts() {
- return childLayouts;
+ public Pos getAlignment() {
+ return alignment;
}
- public void addChildLayout(Layout layout) {
- this.childLayouts.add(layout);
+ public void setAlignment(Pos alignment) {
+ this.alignment = alignment;
}
- public void addChildLayouts(LinkedList layouts) {
- this.childLayouts.addAll(layouts);
+ public void setPadding(Insets padding) {
+ this.padding = padding;
}
- public void addChildLayouts(Layout... layouts) {
- this.childLayouts.addAll(List.of(layouts));
+ public Insets getPadding() {
+ return padding;
}
- public abstract Pane render(Container container);
+ public abstract Node render();
}
diff --git a/src/main/java/me/piitex/renjava/gui/layouts/VerticalLayout.java b/src/main/java/me/piitex/renjava/gui/layouts/VerticalLayout.java
index 439dac9a..e354af0b 100644
--- a/src/main/java/me/piitex/renjava/gui/layouts/VerticalLayout.java
+++ b/src/main/java/me/piitex/renjava/gui/layouts/VerticalLayout.java
@@ -1,16 +1,16 @@
package me.piitex.renjava.gui.layouts;
+import javafx.geometry.Insets;
import javafx.scene.Node;
-import javafx.scene.layout.Pane;
-import javafx.scene.layout.VBox;
-import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.overlays.Overlay;
+import javafx.scene.layout.*;
public class VerticalLayout extends Layout {
+ private final VBox pane;
private double spacing = 10;
public VerticalLayout(double width, double height) {
super(new VBox(), width, height);
+ this.pane = (VBox) getPane();
}
public double getSpacing() {
@@ -22,23 +22,40 @@ public void setSpacing(double spacing) {
}
@Override
- public Pane render(Container container) {
+ public Node render() {
// Clear
- VBox pane = (VBox) getPane();
pane.setSpacing(getSpacing());
pane.setTranslateX(getX());
pane.setTranslateY(getY());
pane.getChildren().clear();
+ if (getWidth() > 0) {
+ pane.setMinWidth(getWidth());
+ }
+ if (getHeight() > 0) {
+ pane.setMinHeight(getHeight());
+ }
- for (Overlay overlay : getOverlays()) {
- Node node = overlay.render();
- pane.getChildren().add(node);
+ if (getPrefWidth() > 0) {
+ pane.setPrefWidth(getPrefWidth());
+ }
+ if (getPrefHeight() > 0) {
+ pane.setPrefHeight(getPrefHeight());
}
- for (Layout layout : getChildLayouts()) {
- pane.getChildren().add(layout.render(container));
+ if (getMaxWidth() > 0) {
+ pane.setMaxWidth(getMaxWidth());
+ }
+ if (getMaxHeight() > 0) {
+ pane.setMaxHeight(getMaxHeight());
+ }
+ if (getAlignment() != null) {
+ pane.setAlignment(getAlignment());
+ }
+ if (getBackgroundColor() != null) {
+ pane.setBackground(new Background(new BackgroundFill(getBackgroundColor(), CornerRadii.EMPTY, Insets.EMPTY)));
}
- return getPane();
+ setStyling(pane);
+ return pane;
}
}
diff --git a/src/main/java/me/piitex/renjava/gui/menus/DefaultMainMenu.java b/src/main/java/me/piitex/renjava/gui/menus/DefaultMainMenu.java
index b1c7b0cd..ac1afb39 100644
--- a/src/main/java/me/piitex/renjava/gui/menus/DefaultMainMenu.java
+++ b/src/main/java/me/piitex/renjava/gui/menus/DefaultMainMenu.java
@@ -10,7 +10,6 @@
import me.piitex.renjava.events.types.MouseClickEvent;
import me.piitex.renjava.events.types.SideMenuBuildEvent;
import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.DisplayOrder;
import me.piitex.renjava.gui.StageType;
import me.piitex.renjava.gui.Window;
import me.piitex.renjava.gui.containers.EmptyContainer;
@@ -20,7 +19,7 @@
import me.piitex.renjava.loggers.RenLogger;
import me.piitex.renjava.gui.prompts.Prompt;
-import java.util.LinkedList;
+import java.util.LinkedHashMap;
public class DefaultMainMenu implements MainMenu {
@@ -28,25 +27,41 @@ public class DefaultMainMenu implements MainMenu {
public Container mainMenu(boolean rightClicked) {
// Empty container behaves like the old menu system.
// It is essentially an empty box which you add overlays to.
+ System.out.println("Using default execute...");
Container menu = new EmptyContainer(RenJava.CONFIGURATION.getWidth(), RenJava.CONFIGURATION.getHeight());
- menu.addOverlay(new ImageOverlay("gui/main_menu.png"));
+
+ menu.addElement(new ImageOverlay("/gui/main_menu.png"));
// Basic text overlay
TextOverlay gameText = new TextOverlay(RenJava.getInstance().getName() + ' ' + RenJava.getInstance().getVersion(), new FontLoader(RenJava.CONFIGURATION.getUiFont(), 36), 1500, 975);
+ gameText.setTextFill(Color.WHITE);
// Add the overlay to the container
- menu.addOverlay(gameText);
+ menu.addElement(gameText);
+
+
+ // FIXME: Something very wrong with the rendering pipe line. Broken when a sub-container is added to a container BEFORE the container is rendered.
+ // For example, Container A which hasn't been added via window.addContainer() Will not render Container B.
+ // Container a = new EmptyContainer();
+ // Container b = new EmptyContainer();
+ // a.addElement(b);
+ // window.addContainer(a);
+ // It will work if the container is rendered before hand.
+ // Container a = new EmptyContainer();
+ // window.addContainer(a);
+ // Container b = new EmptyContainer();
+ // a.addElement(b);
+ //menu.addElement(sideMenu(rightClicked));
return menu;
}
@Override
public Container sideMenu(boolean rightClick) {
- Container menu = new EmptyContainer(1920, 1080, DisplayOrder.HIGH);
+ Container container = new EmptyContainer(1920, 1080);
ImageOverlay imageOverlay = new ImageOverlay("gui/overlay/main_menu.png");
- imageOverlay.setOrder(DisplayOrder.LOW);
- menu.addOverlay(imageOverlay);
+ container.addElement(imageOverlay);
FontLoader uiFont = RenJava.CONFIGURATION.getUiFont();
@@ -59,18 +74,15 @@ public Container sideMenu(boolean rightClick) {
ButtonOverlay aboutButton = new ButtonOverlay(ButtonID.ABOUT.getId(),"About", Color.BLACK, uiFont, Color.TRANSPARENT, Color.TRANSPARENT, hoverColor);
// Create vbox for the buttons. You can also do an HBox
VerticalLayout layout = new VerticalLayout(400, 500);
- layout.setOrder(DisplayOrder.HIGH);
layout.setX(50);
layout.setY(250);
layout.setSpacing(20);
- layout.addOverlays(startButton, loadButton);
+ container.addElement(layout);
+ layout.addElements(startButton, loadButton);
if (rightClick) {
- layout.addOverlays(saveButton);
+ layout.addElement(saveButton);
}
- layout.addOverlays(optionsButton, aboutButton);
-
- // You don't have to add the button overlays just add the layout which already contains the overlays.
- menu.addLayout(layout);
+ layout.addElements(optionsButton, aboutButton);
ButtonOverlay returnButton;
@@ -81,20 +93,19 @@ public Container sideMenu(boolean rightClick) {
}
returnButton.setX(25);
returnButton.setY(980);
- menu.addOverlay(returnButton);
+ container.addElement(returnButton);
- SideMenuBuildEvent sideMenuBuildEvent = new SideMenuBuildEvent(menu);
+ SideMenuBuildEvent sideMenuBuildEvent = new SideMenuBuildEvent(container);
RenJava.getEventHandler().callEvent(sideMenuBuildEvent);
- return menu;
+ return container;
}
@Override
public Container loadMenu(boolean rightClicked, int page, boolean loadMenu) {
- Container menu = new EmptyContainer(RenJava.CONFIGURATION.getWidth(), RenJava.CONFIGURATION.getHeight(), DisplayOrder.NORMAL);
+ Container menu = new EmptyContainer(RenJava.CONFIGURATION.getWidth(), RenJava.CONFIGURATION.getHeight());
ImageOverlay imageOverlay = new ImageOverlay("gui/main_menu.png");
- imageOverlay.setOrder(DisplayOrder.LOW);
- menu.addOverlay(imageOverlay);
+ menu.addElement(imageOverlay);
TextOverlay menuText;
if (loadMenu) {
@@ -105,7 +116,7 @@ public Container loadMenu(boolean rightClicked, int page, boolean loadMenu) {
menuText.setX(RenJava.CONFIGURATION.getMidPoint().getKey());
menuText.setY(50);
- menu.addOverlay(menuText);
+ menu.addElement(menuText);
// Setup pagination.
// 6 save slots per page
@@ -126,15 +137,15 @@ public Container loadMenu(boolean rightClicked, int page, boolean loadMenu) {
Window gameWindow = RenJava.getInstance().getGameWindow();
- LinkedList containers = new LinkedList<>(gameWindow.getContainers());
+ //LinkedList containers = new LinkedList<>(gameWindow.getContainers().values());
+ LinkedHashMap containers = new LinkedHashMap<>(gameWindow.getContainers());
while (index <= maxSavesPerPage) {
Save save = Save.createSave(page, index);
// Create a button box
VerticalLayout buttonBox = new VerticalLayout(414, 320);
ButtonOverlay loadButton = savePreview(save, page, index);
- loadButton.setOrder(DisplayOrder.HIGH);
- buttonBox.addOverlay(loadButton);
+ buttonBox.addElement(loadButton);
FontLoader bottomFont = new FontLoader(RenJava.CONFIGURATION.getUiFont(), 12);
TextOverlay createdTime = new TextOverlay(save.getLocalizedCreationDate(), bottomFont);
@@ -143,35 +154,34 @@ public Container loadMenu(boolean rightClicked, int page, boolean loadMenu) {
createdTime.setY(-80); // Moves the text up a little in the vbox
createdTime.setX(120); // Moves over to the center hopefully
- buttonBox.addOverlay(createdTime);
+ buttonBox.addElement(createdTime);
if (save.getName() != null && !save.getName().isEmpty() && !save.getName().equalsIgnoreCase("null")) {
TextOverlay saveName = new TextOverlay(save.getName(), bottomFont);
saveName.onClick(loadButton.getOnClick());
saveName.setY(-80);
saveName.setX(120);
- buttonBox.addOverlay(saveName);
+ buttonBox.addElement(saveName);
}
if (index <= 3) {
- topLayout.addChildLayout(buttonBox);
+ topLayout.addElement(buttonBox);
} else {
- bottomLayout.addChildLayout(buttonBox);
+ bottomLayout.addElement(buttonBox);
}
index++;
}
// Once the fetching is done re-render the view.
- gameWindow.setContainers(containers);
gameWindow.render();
- rootLayout.addChildLayout(topLayout);
- rootLayout.addChildLayout(bottomLayout);
+ rootLayout.addElement(topLayout);
+ rootLayout.addElement(bottomLayout);
rootLayout.setX(500);
rootLayout.setY(250);
- menu.addLayout(rootLayout);
+ menu.addElement(rootLayout);
// Add Page buttons below.
// There should be 8 per view.
@@ -197,20 +207,19 @@ public Container loadMenu(boolean rightClicked, int page, boolean loadMenu) {
Container load = RenJava.getInstance().getMainMenu().loadMenu(rightClicked, newPage, true); //TODO: Pages
Container side = RenJava.getInstance().getMainMenu().sideMenu(rightClicked);
- side.setOrder(DisplayOrder.HIGH);
- load.addContainers(side);
+ load.addElement(side, 2);
gameWindow.clearContainers();
gameWindow.addContainer(load);
gameWindow.render();
});
- pageLayout.addOverlay(pageButton);
+ pageLayout.addElement(pageButton);
}
pageLayout.setX(1000);
pageLayout.setY(950);
- menu.addLayout(pageLayout);
+ menu.addElement(pageLayout);
return menu;
}
@@ -219,8 +228,7 @@ public Container loadMenu(boolean rightClicked, int page, boolean loadMenu) {
public Container settingMenu(boolean rightClicked) {
Container menu = new EmptyContainer(RenJava.CONFIGURATION.getWidth(), RenJava.CONFIGURATION.getHeight());
ImageOverlay imageOverlay = new ImageOverlay("gui/main_menu.png");
- imageOverlay.setOrder(DisplayOrder.LOW);
- menu.addOverlay(imageOverlay);
+ menu.addElement(imageOverlay);
Color themeColor = RenJava.CONFIGURATION.getThemeColor();
Color subColor = RenJava.CONFIGURATION.getSubColor();
@@ -242,26 +250,26 @@ public Container settingMenu(boolean rightClicked) {
TextOverlay displayText = new TextOverlay("Display", themeColor, RenJava.CONFIGURATION.getUiFont(), 0, 0);
ButtonOverlay windowButton = new ButtonOverlay("windowed-display", "Windowed", subColor, RenJava.CONFIGURATION.getUiFont(), 0,0);
ButtonOverlay fullscreenButton = new ButtonOverlay("windowed-fullscreen", "Fullscreen", subColor, RenJava.CONFIGURATION.getUiFont(), 0,0);
- displayBox.addOverlays(displayText, windowButton, fullscreenButton);
+ displayBox.addElements(displayText, windowButton, fullscreenButton);
VerticalLayout rollbackBox = new VerticalLayout(300, 400);
TextOverlay rollbackText = new TextOverlay("Rollback", themeColor, RenJava.CONFIGURATION.getUiFont(), 0, 0);
ButtonOverlay disabledButton = new ButtonOverlay("disabled-rollback", "Disabled", subColor, RenJava.CONFIGURATION.getUiFont(), 0,0);
ButtonOverlay leftButton = new ButtonOverlay("left-rollback", "Left", subColor, RenJava.CONFIGURATION.getUiFont(), 0,0);
ButtonOverlay rightButton = new ButtonOverlay("right-rollback", "Right", subColor, RenJava.CONFIGURATION.getUiFont(), 0,0);
- rollbackBox.addOverlays(rollbackText, disabledButton, leftButton, rightButton);
+ rollbackBox.addElements(rollbackText, disabledButton, leftButton, rightButton);
VerticalLayout skipBox = new VerticalLayout(300, 400);
TextOverlay skipText = new TextOverlay("Skip", themeColor, RenJava.CONFIGURATION.getUiFont(), 0, 0);
ButtonOverlay unseenTextButton = new ButtonOverlay("unseen-skip", "Unseen Text", subColor, RenJava.CONFIGURATION.getUiFont(), 0,0);
ButtonOverlay afterChoicesButton = new ButtonOverlay("after-skip", "After Choices", subColor, RenJava.CONFIGURATION.getUiFont(), 0,0);
ButtonOverlay transitionButton = new ButtonOverlay("transitions-skip", "Transitions", subColor, RenJava.CONFIGURATION.getUiFont(), 0,0);
- skipBox.addOverlays(skipText, unseenTextButton, afterChoicesButton, transitionButton);
+ skipBox.addElements(skipText, unseenTextButton, afterChoicesButton, transitionButton);
// Add all to root layout
- rootLayout.addChildLayouts(displayBox, rollbackBox, skipBox);
+ rootLayout.addElements(displayBox, rollbackBox, skipBox);
- menu.addLayout(rootLayout);
+ menu.addElement(rootLayout);
// Music sliders
VerticalLayout soundRoot = new VerticalLayout(1000, 1000);
@@ -275,7 +283,7 @@ public Container settingMenu(boolean rightClicked) {
masterVolumeSlider.onSliderMove(event -> {
RenJava.SETTINGS.setMasterVolume(event.getValue());
});
- masterBox.addOverlays(masterVolumeText, masterVolumeSlider);
+ masterBox.addElements(masterVolumeText, masterVolumeSlider);
VerticalLayout musicBox = new VerticalLayout(1000, 100);
TextOverlay musicVolumeText = new TextOverlay("Music Volume", themeColor, RenJava.CONFIGURATION.getUiFont(), 0, 0);
@@ -283,7 +291,7 @@ public Container settingMenu(boolean rightClicked) {
musicVolumeSlider.onSliderMove(event -> {
RenJava.SETTINGS.setMusicVolume(event.getValue());
});
- musicBox.addOverlays(musicVolumeText, musicVolumeSlider);
+ musicBox.addElements(musicVolumeText, musicVolumeSlider);
VerticalLayout soundBox = new VerticalLayout(1000, 100);
TextOverlay soundVolumeText = new TextOverlay("Sound Volume", themeColor, RenJava.CONFIGURATION.getUiFont(), 0, 0);
@@ -291,7 +299,7 @@ public Container settingMenu(boolean rightClicked) {
soundVolumeSlider.onSliderMove(event -> {
RenJava.SETTINGS.setSoundVolume(event.getValue());
});
- soundBox.addOverlays(soundVolumeText, soundVolumeSlider);
+ soundBox.addElements(soundVolumeText, soundVolumeSlider);
VerticalLayout voiceBox = new VerticalLayout(1000, 100);
TextOverlay voiceVolumeText = new TextOverlay("Voice Volume", themeColor, RenJava.CONFIGURATION.getUiFont(), 0, 0);
@@ -299,12 +307,12 @@ public Container settingMenu(boolean rightClicked) {
voiceVolumeSlider.onSliderMove(event -> {
RenJava.SETTINGS.setVoiceVolume(event.getValue());
});
- voiceBox.addOverlays(voiceVolumeText, voiceVolumeSlider);
+ voiceBox.addElements(voiceVolumeText, voiceVolumeSlider);
- soundRoot.addChildLayouts(masterBox, musicBox, soundBox, voiceBox);
+ soundRoot.addElements(masterBox, musicBox, soundBox, voiceBox);
- menu.addLayout(soundRoot);
+ menu.addElement(soundRoot);
return menu;
}
@@ -312,8 +320,9 @@ public Container settingMenu(boolean rightClicked) {
@Override
public Container aboutMenu(boolean rightClicked) {
Container menu = new EmptyContainer(RenJava.CONFIGURATION.getWidth(), RenJava.CONFIGURATION.getHeight());
+
TextOverlay spacer = new TextOverlay("\n");
- menu.addOverlay(new ImageOverlay("gui/main_menu.png"));
+ menu.addElement(new ImageOverlay("gui/main_menu.png"));
FontLoader font = new FontLoader(RenJava.CONFIGURATION.getDefaultFont().getFont(), 24);
TextFlowOverlay aboutText = new TextFlowOverlay("", 1300, 500);
@@ -322,7 +331,7 @@ public Container aboutMenu(boolean rightClicked) {
aboutText.setFont(font);
aboutText.setX(500);
aboutText.setY(150);
- menu.addOverlay(aboutText);
+ menu.addElement(aboutText);
// If you modify the about you must include the license information.
TextFlowOverlay licenseText = new TextFlowOverlay("License Information. Some of the licenses below are required to be disclosed. Modify the below only to include additional required licenses.", 1300, 700);
@@ -357,16 +366,10 @@ public Container aboutMenu(boolean rightClicked) {
licenseText.add(new TextOverlay("\tOshi is licensed under MIT: "));
licenseText.add(new HyperLinkOverlay("https://github.com/oshi/oshi/blob/master/LICENSE"));
- menu.addOverlay(licenseText);
+ menu.addElement(licenseText);
- TextFlowOverlay buildInfo = new TextFlowOverlay("Just because the software this game uses may be free for use does not mean you have any right to re-distribute. " +
- "The author holds and retrains rights to distribute and sell their game as they wish. " +
- "Consumers hold no rights when re-distributing or publishing this game without explicit permission from the author. " +
- "Consumers also have no ownership over this digital product and must follow the terms provided by the author. " +
- "If no terms were provided then you must use the following. You are not allowed to re-sell or re-distribute this game. " +
- "If you paid for this game you did not pay for ownership but rather permission to use this game. This permission can be revoked at any time for any reason." +
- "These terms also apply to any addons that may come with the game.",1300, 800);
+ TextFlowOverlay buildInfo = new TextFlowOverlay("You must comply with any user agreement set by the seller and or developer. Within the license set by RenJava, developers can revoke your ownership of this product if any agreement is breached. Do not re-distribute this game without expressed consent of the rightful owner.",1300, 800);
buildInfo.setX(500);
buildInfo.setY(600);
buildInfo.setFont(new FontLoader(font, 20));
@@ -379,7 +382,7 @@ public Container aboutMenu(boolean rightClicked) {
buildInfo.add(new TextOverlay("Author: " + RenJava.getInstance().getAuthor()));
buildInfo.add(spacer);
- menu.addOverlay(buildInfo);
+ menu.addElement(buildInfo);
return menu;
}
@@ -394,7 +397,7 @@ public ButtonOverlay savePreview(Save save, int page, int index) {
loadButton = new ButtonOverlay("save-" + index, saveImage, 0, 0, 404, 319);
loadButton.setAlignGraphicToBox(false);
- ImageOverlay preview = save.buildPreview(page);
+ ImageOverlay preview = save.buildPreview();
if (preview != null) {
preview.setWidth(384);
preview.setHeight(215);
@@ -471,20 +474,20 @@ public ButtonOverlay savePreview(Save save, int page, int index) {
Container container = RenJava.getInstance().getMainMenu().loadMenu(RenJava.PLAYER.isRightClickMenu(), page, false);
Container side = RenJava.getInstance().getMainMenu().sideMenu(RenJava.PLAYER.isRightClickMenu());
- container.addContainer(side);
+ container.addElement(side);
gameWindow.addContainer(container);
gameWindow.render();
});
- prompt.addOverlay(confirm);
+ prompt.addElement(confirm);
ButtonOverlay cancel = new ButtonOverlay("cancel", "Cancel", Color.WHITE, RenJava.CONFIGURATION.getUiFont());
cancel.setX(700);
cancel.setY(300);
- prompt.addOverlay(cancel);
+ prompt.addElement(cancel);
cancel.onClick(event1 -> {
prompt.closeWindow();
@@ -517,12 +520,12 @@ public ButtonOverlay savePreview(Save save, int page, int index) {
// Re-render
RenJava.PLAYER.setCurrentStageType(StageType.SAVE_MENU);
Container menu = loadMenu(false, 1, false); // Builds first page
- menu.addContainers(sideMenu(true));
+ menu.addElement(sideMenu(true));
RenJava.getInstance().getGameWindow().clearContainers();
RenJava.getInstance().getGameWindow().addContainer(menu);
RenJava.getInstance().getGameWindow().render();
});
- prompt.addOverlay(confirm);
+ prompt.addElement(confirm);
ButtonOverlay cancel = new ButtonOverlay("cancel", "Cancel", Color.WHITE, RenJava.CONFIGURATION.getUiFont());
cancel.setX(700);
@@ -530,7 +533,7 @@ public ButtonOverlay savePreview(Save save, int page, int index) {
cancel.onClick(event1 -> {
prompt.closeWindow();
});
- prompt.addOverlay(cancel);
+ prompt.addElement(cancel);
prompt.render();
} else {
@@ -539,7 +542,7 @@ public ButtonOverlay savePreview(Save save, int page, int index) {
// Re-render
RenJava.PLAYER.setCurrentStageType(StageType.SAVE_MENU);
Container menu = loadMenu(false, page, false); // Builds first page
- menu.addContainers(sideMenu(true));
+ menu.addElement(sideMenu(true));
RenJava.getInstance().getGameWindow().clearContainers();
RenJava.getInstance().getGameWindow().addContainer(menu);
RenJava.getInstance().getGameWindow().render();
diff --git a/src/main/java/me/piitex/renjava/gui/menus/MainMenu.java b/src/main/java/me/piitex/renjava/gui/menus/MainMenu.java
index 5509dbb7..65617c0a 100644
--- a/src/main/java/me/piitex/renjava/gui/menus/MainMenu.java
+++ b/src/main/java/me/piitex/renjava/gui/menus/MainMenu.java
@@ -26,7 +26,7 @@ public interface MainMenu {
* // Add overlays to the main menu
* ImageOverlay backgroundImage = new ImageOverlay("gui/main_menu.png");
* backgroundImage.setOrder(DisplayOrder.LOW); // Sends the background image to the back of the container.
- * container.addOverlay(backgroundImage);
+ * container.addElement(backgroundImage);
*
* return container;
* }
diff --git a/src/main/java/me/piitex/renjava/gui/overlays/ButtonOverlay.java b/src/main/java/me/piitex/renjava/gui/overlays/ButtonOverlay.java
index b6b131cb..54873807 100644
--- a/src/main/java/me/piitex/renjava/gui/overlays/ButtonOverlay.java
+++ b/src/main/java/me/piitex/renjava/gui/overlays/ButtonOverlay.java
@@ -489,8 +489,11 @@ public Button build() {
}
}
- if (getX() != 0 && getY() != 0) {
+ if (getX() != 0) {
button.setTranslateX(getX());
+ }
+
+ if (getY() != 0) {
button.setTranslateY(getY());
}
diff --git a/src/main/java/me/piitex/renjava/gui/overlays/ImageOverlay.java b/src/main/java/me/piitex/renjava/gui/overlays/ImageOverlay.java
index 856b23fc..4e19ad53 100644
--- a/src/main/java/me/piitex/renjava/gui/overlays/ImageOverlay.java
+++ b/src/main/java/me/piitex/renjava/gui/overlays/ImageOverlay.java
@@ -4,10 +4,7 @@
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
-import me.piitex.renjava.RenJava;
import me.piitex.renjava.api.loaders.ImageLoader;
-import me.piitex.renjava.api.exceptions.ImageNotFoundException;
-import me.piitex.renjava.loggers.RenLogger;
/**
* The ImageOverlay is a visual element used to display images. The ImageOverlay uses the {@link ImageLoader} to load images into the application. The image must be in either the classpath or the game directory to be loaded.
@@ -38,8 +35,7 @@
* image.setHeight(1080);
* }
*
- * Make sure images are compressed to normal sizes. Images which have expressive sizes will result in longer rendering times and slower performance.
- * If the image file could not be located or found it will throw {@link ImageNotFoundException}.
+ * Make sure images are compressed to normal sizes. Images which have large sizes will result in longer rendering times and slower performance.
*
* @see ImageLoader
* @see Region
@@ -82,15 +78,11 @@ public ImageOverlay(WritableImage image) {
* @see ImageLoader
*/
public ImageOverlay(ImageLoader imageLoader) {
- try {
- this.image = imageLoader.build();
- } catch (ImageNotFoundException e) {
- RenLogger.LOGGER.error(e.getMessage(), e);
- RenJava.writeStackTrace(e);
- }
+ this.image = imageLoader.build();
this.width = image.getWidth();
this.height = image.getHeight();
this.fileName = imageLoader.getFile().getName();
+ this.path = imageLoader.getFile().getAbsolutePath();
}
/**
@@ -123,12 +115,7 @@ public ImageOverlay(ImageLoader imageLoader) {
*/
public ImageOverlay(String imagePath) {
ImageLoader loader = new ImageLoader(imagePath);
- try {
- this.image = loader.build();
- } catch (ImageNotFoundException e) {
- RenLogger.LOGGER.error(e.getMessage(), e);
- RenJava.writeStackTrace(e);
- }
+ this.image = loader.build();
this.fileName = loader.getFile().getName();
this.path = imagePath;
this.width = image.getWidth();
@@ -167,16 +154,10 @@ public ImageOverlay(String imagePath) {
* }
*
* @param imagePath The path to the file from the game directory.
- * @exception ImageNotFoundException if the file could not be located. The engine will handle the throw in the logger to prevent crashing.
*/
public ImageOverlay(String directory, String imagePath) {
ImageLoader loader = new ImageLoader(directory, imagePath);
- try {
- this.image = loader.build();
- } catch (ImageNotFoundException e) {
- RenLogger.LOGGER.error(e.getMessage(), e);
- RenJava.writeStackTrace(e);
- }
+ this.image = loader.build();
this.fileName = loader.getFile().getName();
this.width = image.getWidth();
this.height = image.getHeight();
@@ -206,12 +187,7 @@ public ImageOverlay(Image image, double x, double y) {
* @see ImageLoader
*/
public ImageOverlay(ImageLoader imageLoader, double x, double y) {
- try {
- this.image = imageLoader.build();
- } catch (ImageNotFoundException e) {
- RenLogger.LOGGER.error(e.getMessage(), e);
- RenJava.writeStackTrace(e);
- }
+ this.image = imageLoader.build();
this.width = image.getWidth();
this.height = image.getHeight();
this.fileName = imageLoader.getFile().getName();
@@ -227,6 +203,10 @@ public String getFileName() {
return fileName;
}
+ public String getPath() {
+ return path;
+ }
+
public boolean isPreserveRatio() {
return preserveRatio;
}
diff --git a/src/main/java/me/piitex/renjava/gui/overlays/Overlay.java b/src/main/java/me/piitex/renjava/gui/overlays/Overlay.java
index 2cefdf54..34b148c8 100644
--- a/src/main/java/me/piitex/renjava/gui/overlays/Overlay.java
+++ b/src/main/java/me/piitex/renjava/gui/overlays/Overlay.java
@@ -1,11 +1,12 @@
package me.piitex.renjava.gui.overlays;
+import javafx.application.Platform;
import javafx.scene.Node;
import me.piitex.renjava.RenJava;
import me.piitex.renjava.api.scenes.transitions.Transitions;
import me.piitex.renjava.events.types.*;
import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.DisplayOrder;
+import me.piitex.renjava.gui.Element;
import me.piitex.renjava.gui.Window;
import me.piitex.renjava.gui.overlays.events.IOverlayClick;
import me.piitex.renjava.gui.overlays.events.IOverlayClickRelease;
@@ -34,7 +35,7 @@
* Container container = new EmptyContainer(x, y, width, height, displayOrder);
*
* // Add the overlay to the container.
- * container.addOverlay(overlay);
+ * container.addElement(overlay);
*
* // Add the container to the window if needed.
* window.addContainer(container);
@@ -58,10 +59,9 @@
* }
*
*/
-public abstract class Overlay {
+public abstract class Overlay extends Element {
private double x,y;
private double scaleX, scaleY;
- private DisplayOrder order = DisplayOrder.NORMAL;
private IOverlayHover iOverlayHover;
private IOverlayHoverExit iOverlayHoverExit;
@@ -104,14 +104,6 @@ public void setScaleY(double scaleY) {
this.scaleY = scaleY;
}
- public DisplayOrder getOrder() {
- return order;
- }
-
- public void setOrder(DisplayOrder order) {
- this.order = order;
- }
-
public void onClick(IOverlayClick iOverlayClick) {
this.iOverlayClick = iOverlayClick;
}
@@ -165,7 +157,27 @@ public Overlay addTransition(Transitions transitions) {
* Converts the overlay into a {@link Node} which is used for the JavaFX API.
* @return The converted {@link Node} for the overlay.
*/
- public abstract Node render();
+ protected abstract Node render();
+
+ @Override
+ public Node assemble() {
+ Node node = render();
+ setNode(node);
+
+ // Starting to implement sub-thread loading.
+ // Input controls directly access JavaFX's event listeners.
+ // Event listeners are required to be executed on the FXThread.
+ // Check if the current thread is the FXThread, if not run it on the FXThread.
+ if (!Platform.isFxApplicationThread()) {
+ Platform.runLater(() -> {
+ setInputControls(node);
+ });
+ } else {
+ setInputControls(node);
+ }
+
+ return node;
+ }
public void renderTransitions(Node node) {
for (Transitions transitions1 : getTransitions()) {
diff --git a/src/main/java/me/piitex/renjava/gui/overlays/TextFlowOverlay.java b/src/main/java/me/piitex/renjava/gui/overlays/TextFlowOverlay.java
index c73d0c30..4bc6e8a2 100644
--- a/src/main/java/me/piitex/renjava/gui/overlays/TextFlowOverlay.java
+++ b/src/main/java/me/piitex/renjava/gui/overlays/TextFlowOverlay.java
@@ -117,27 +117,31 @@ public TextFlow build() {
TextFlow textFlow = new TextFlow();
for (Overlay overlay : texts) {
// Check node type
- if (overlay instanceof TextOverlay text) {
- text.setText(text.getText().replace("\\n", System.lineSeparator()));
- if (font != null) {
- text.setFont(font);
+ switch (overlay) {
+ case TextOverlay text -> {
+ text.setText(text.getText().replace("\\n", System.lineSeparator()));
+ if (font != null && text.getFontLoader() == null) {
+ text.setFont(font);
+ }
+ text.setTextFill(textFillColor);
}
- text.setTextFill(textFillColor);
- } else if (overlay instanceof HyperLinkOverlay hyperlink) {
- if (font != null) {
- hyperlink.setFont(font);
+ case HyperLinkOverlay hyperlink -> {
+ if (font != null) {
+ hyperlink.setFont(font);
+ }
}
- } else if (overlay instanceof ButtonOverlay button) {
- if (font != null) {
- button.setFont(font);
+ case ButtonOverlay button -> {
+ if (font != null) {
+ button.setFont(font);
+ }
+ button.setTextFill(textFillColor);
}
- button.setTextFill(textFillColor);
- } else if (overlay instanceof InputFieldOverlay inputField) {
- if (font != null) {
- inputField.setFont(font);
+ case InputFieldOverlay inputField -> {
+ if (font != null) {
+ inputField.setFont(font);
+ }
}
- } else {
- RenLogger.LOGGER.warn("Unsupported overlay in TextFlow. {}", overlay.toString());
+ default -> RenLogger.LOGGER.warn("Unsupported overlay in TextFlow. {}", overlay.toString());
}
textFlow.getChildren().add(overlay.render());
diff --git a/src/main/java/me/piitex/renjava/gui/overlays/TextOverlay.java b/src/main/java/me/piitex/renjava/gui/overlays/TextOverlay.java
index 73eb3237..028c1785 100644
--- a/src/main/java/me/piitex/renjava/gui/overlays/TextOverlay.java
+++ b/src/main/java/me/piitex/renjava/gui/overlays/TextOverlay.java
@@ -7,20 +7,11 @@
import me.piitex.renjava.api.loaders.FontLoader;
/**
- * The TextOverlay is a visual element which displays text. The overlay supports font, positioning, and color.
- * This overlay has a defined {@link Region}.
+ * Represents a graphical overlay for displaying text within a GUI.
*
- *
- * {@code
- * TextOverlay overlay = new TextOverlay("Text");
- * overlay.setFont(font);
- * overlay.setTextFill(color);
- * overlay.setX(x);
- * overlay.setY(y);
- * overlay.setWidth(width);
- * overlay.setHeight(height);
- * }
- *
+ * The {@code TextOverlay} class allows customization of text appearance and position, such as its font, color, size, and location.
+ * It supports the use of external font loaders and integrates seamlessly with the GUI framework.
+ *
*/
public class TextOverlay extends Overlay implements Region {
private String text;
@@ -31,18 +22,18 @@ public class TextOverlay extends Overlay implements Region {
private boolean strikeout;
/**
- * Creates an overlay of the text.
- * @param text The text for the overlay.
+ * Constructs a TextOverlay with the specified text content.
+ *
+ * @param text The text to display in this overlay.
*/
public TextOverlay(String text) {
this.text = text;
}
/**
- * Creates a TextOverlay with a specific font.
- * @param text The text for the overlay.
- * @param fontLoader The font to be used.
- * @see FontLoader
+ * Constructs a TextOverlay with the specified text and font loader.
+ * @param text The text to display in this overlay.
+ * @param fontLoader The font loader used to set the font for the text.
*/
public TextOverlay(String text, FontLoader fontLoader) {
this.text = text;
@@ -50,10 +41,9 @@ public TextOverlay(String text, FontLoader fontLoader) {
}
/**
- * Creates a TextOverlay with a specific text color.
- * @param text The text for the overlay.
- * @param textFillColor The color of the text.
- * @see Color
+ * Constructs a TextOverlay with the specified text and color.
+ * @param text The text to display in this overlay.
+ * @param textFillColor The color used to fill the text characters.
*/
public TextOverlay(String text, Color textFillColor) {
this.text = text;
@@ -61,12 +51,10 @@ public TextOverlay(String text, Color textFillColor) {
}
/**
- * Creates a TextOverlay with a specific color and font.
- * @param text The text for the overlay.
- * @param textFillColor The color of the text.
- * @param fontLoader The font to be used.
- * @see Color
- * @see FontLoader
+ * Constructs a TextOverlay with the specified text, color, and font loader.
+ * @param text The text to display in this overlay.
+ * @param textFillColor The color used to fill the text characters.
+ * @param fontLoader The font loader used to set the font for the text.
*/
public TextOverlay(String text, Color textFillColor, FontLoader fontLoader) {
this.text = text;
@@ -75,13 +63,11 @@ public TextOverlay(String text, Color textFillColor, FontLoader fontLoader) {
}
/**
- * Creates a TextOverlay with a specific font and positioning.
- * @param text The text for the overlay.
- * @param fontLoader The font to be used.
- * @param x The x position of the overlay.
- * @param y The y position of the overlay.
- * @see FontLoader
- * @see Region
+ * Constructs a TextOverlay with the specified text, font loader, and position.
+ * @param text The text to display in this overlay.
+ * @param fontLoader The font loader used to set the font for the text.
+ * @param x The x-coordinate of the overlay's position.
+ * @param y The y-coordinate of the overlay's position.
*/
public TextOverlay(String text, FontLoader fontLoader, double x, double y) {
this.text = text;
@@ -91,15 +77,12 @@ public TextOverlay(String text, FontLoader fontLoader, double x, double y) {
}
/**
- * Creates a TextOverlay with a specific color, font, and positioning.
- * @param text The text for the overlay.
- * @param textFillColor The color of the text.
- * @param fontLoader The font to be used.
- * @param x The x position of the overlay.
- * @param y The y position of the overlay.
- * @see Color
- * @see FontLoader
- * @see Region
+ * Constructs a TextOverlay with the specified text, color, font loader, and position.
+ * @param text The text to display in this overlay.
+ * @param textFillColor The color used to fill the text characters.
+ * @param fontLoader The font loader used to set the font for the text.
+ * @param x The x-coordinate of the overlay's position.
+ * @param y The y-coordinate of the overlay's position.
*/
public TextOverlay(String text, Color textFillColor, FontLoader fontLoader, int x, int y) {
this.text = text;
@@ -109,6 +92,14 @@ public TextOverlay(String text, Color textFillColor, FontLoader fontLoader, int
setY(y);
}
+ /**
+ * Renders the overlay as a JavaFX {@link Text} node.
+ *
+ * The method applies all configured properties, such as font, color, position, and effects, to the text.
+ *
+ *
+ * @return A {@link Node} representing the rendered text.
+ */
@Override
public Node render() {
Text text = new Text(getText());
diff --git a/src/main/java/me/piitex/renjava/gui/prompts/Prompt.java b/src/main/java/me/piitex/renjava/gui/prompts/Prompt.java
index 3cabb62d..681496da 100644
--- a/src/main/java/me/piitex/renjava/gui/prompts/Prompt.java
+++ b/src/main/java/me/piitex/renjava/gui/prompts/Prompt.java
@@ -7,11 +7,11 @@
import javafx.stage.StageStyle;
import me.piitex.renjava.RenJava;
import me.piitex.renjava.gui.Container;
-import me.piitex.renjava.gui.DisplayOrder;
+import me.piitex.renjava.gui.Element;
import me.piitex.renjava.gui.Window;
+import me.piitex.renjava.gui.WindowBuilder;
import me.piitex.renjava.gui.containers.EmptyContainer;
import me.piitex.renjava.gui.overlays.ImageOverlay;
-import me.piitex.renjava.gui.overlays.Overlay;
import me.piitex.renjava.gui.overlays.TextFlowOverlay;
import java.util.LinkedList;
@@ -23,14 +23,22 @@
public class Prompt {
private final String message;
- private final LinkedList overlays = new LinkedList<>();
+ private final LinkedList elements = new LinkedList<>();
- private final Window promptWindow = new Window("", StageStyle.UNDECORATED, null, 900, 375, false);
+ private Window promptWindow = new WindowBuilder("").setStageStyle(StageStyle.UNDECORATED).setDimensions(900, 375).setScale(false).build();
+
+ private double x= 900, y = 400;
+
+ private int width, height;
private boolean blockMainWindow = true;
+ private boolean anchorToGame = true;
+
private final TextFlowOverlay textFlowOverlay;
+ private Container cachedContainer;
+
public Prompt(String message) {
this.message = message;
@@ -43,6 +51,38 @@ public String getMessage() {
return message;
}
+ public double getX() {
+ return x;
+ }
+
+ public void setX(double x) {
+ this.x = x;
+ }
+
+ public double getY() {
+ return y;
+ }
+
+ public void setY(double y) {
+ this.y = y;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
public TextFlowOverlay getTextFlowOverlay() {
return textFlowOverlay;
}
@@ -55,51 +95,85 @@ public void setBlockMainWindow(boolean blockMainWindow) {
this.blockMainWindow = blockMainWindow;
}
- public LinkedList getOverlays() {
- return overlays;
+ public boolean isAnchorToGame() {
+ return anchorToGame;
}
- public void addOverlay(Overlay overlay) {
- this.overlays.add(overlay);
+ public void setAnchorToGame(boolean anchorToGame) {
+ this.anchorToGame = anchorToGame;
+ }
+
+ public LinkedList getElements() {
+ return elements;
+ }
+
+ public void addElement(Element element) {
+ this.elements.add(element);
}
public Container build() {
- Container container = new EmptyContainer(900, 375);
- ImageOverlay background = new ImageOverlay("gui/frame.png");
- background.setOrder(DisplayOrder.LOW);
- container.addOverlay(background);
+ if (cachedContainer == null) {
+ Container container = new EmptyContainer(width, height);
- textFlowOverlay.setTextFillColor(Color.WHITE);
- textFlowOverlay.setY(50);
- textFlowOverlay.setX(50);
+ container.setX(x);
+ container.setY(y);
+ ImageOverlay background = new ImageOverlay("gui/frame.png");
+ background.setIndex(0);
+ container.addElement(background);
- container.addOverlay(textFlowOverlay);
+ textFlowOverlay.setTextFillColor(Color.WHITE);
+ textFlowOverlay.setY(50);
+ textFlowOverlay.setX(50);
- container.addOverlays(overlays);
+ container.addElement(textFlowOverlay);
- return container;
+ container.addElements(elements);
+
+ this.cachedContainer = container;
+ }
+
+ return cachedContainer;
}
public void render() {
// Prompts create a new window which blocks input on the game window.
- promptWindow.addContainers(build());
+ if (!isAnchorToGame()) {
+ promptWindow.addContainer(build());
- if (blockMainWindow) {
- promptWindow.getStage().initModality(Modality.WINDOW_MODAL);
promptWindow.getStage().initOwner(RenJava.getInstance().getGameWindow().getStage());
- }
- handleInput();
- promptWindow.render();
+ if (blockMainWindow) {
+ promptWindow.getStage().initModality(Modality.APPLICATION_MODAL);
+ }
+
+ handleInput();
+ promptWindow.render();
+ } else {
+ // Add to current window
+ Window window = RenJava.getInstance().getGameWindow();
+ window.addContainer(build());
+ window.render();
+ }
}
public Window getPromptWindow() {
return promptWindow;
}
+ public void setPromptWindow(Window promptWindow) {
+ this.promptWindow = promptWindow;
+ }
+
public void closeWindow() {
- promptWindow.close();
+ if (anchorToGame) {
+ // Can throw null error.
+ Window window = RenJava.getInstance().getGameWindow();
+ window.removeContainer(cachedContainer);
+ window.render();
+ } else {
+ promptWindow.close();
+ }
}
private void handleInput() {
diff --git a/src/main/java/me/piitex/renjava/utils/LimitedHashMap.java b/src/main/java/me/piitex/renjava/utils/LimitedHashMap.java
index 626fd9eb..6f4d430a 100644
--- a/src/main/java/me/piitex/renjava/utils/LimitedHashMap.java
+++ b/src/main/java/me/piitex/renjava/utils/LimitedHashMap.java
@@ -47,10 +47,7 @@ public V put(K key, V value) {
if (map.size() < limit) {
map.put(key, value);
} else {
- Entry remove = map.entrySet().stream().findFirst().orElse(null);
- if (remove != null) {
- map.remove(remove.getKey());
- }
+ map.entrySet().stream().findFirst().ifPresent(remove -> map.remove(remove.getKey()));
}
return value;
}
@@ -63,7 +60,7 @@ public V remove(Object key) {
@Override
public void putAll(@NotNull Map extends K, ? extends V> m) {
m.forEach((k, v) -> {
- if (size() < limit) {
+ if (map.size() < limit) {
map.put(k, v);
} else {
map.entrySet().stream().findFirst().ifPresent(remove -> map.remove(remove.getKey()));
diff --git a/src/main/java/me/piitex/renjava/utils/MDUtils.java b/src/main/java/me/piitex/renjava/utils/MDUtils.java
index f7e8c217..f3d88be2 100644
--- a/src/main/java/me/piitex/renjava/utils/MDUtils.java
+++ b/src/main/java/me/piitex/renjava/utils/MDUtils.java
@@ -30,6 +30,22 @@ public static String getFileCheckSum(File file) {
}
}
+ /**
+ * Generates a game id by converting the projects name and author into an integer.
+ * Every character in the string is converted to a number. The number is then multiplied by the position in the string and the added to a sum.
+ *
+ * String
+ * s = 239 * 1
+ * t = 241 * 2
+ * r = 228 * 3
+ * i = 138 * 4
+ * n = 184 * 5
+ * g = 116 * 6
+ * sum = s + t + r + i + n + g
+ *
+ * @param fullLength
+ * @return
+ */
public static int getGameID(String fullLength) {
// The game id is a sort of unique id given to each game.
// This method takes the characters of the name and author and converts them to numbers.