From e8df6bdcf517bc157a8b17c2b363d33f04118bef Mon Sep 17 00:00:00 2001 From: piitex Date: Tue, 18 Nov 2025 09:00:37 -0600 Subject: [PATCH 01/78] Removed deprecated URL call. --- src/main/java/me/piitex/os/GitHubUtil.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/me/piitex/os/GitHubUtil.java b/src/main/java/me/piitex/os/GitHubUtil.java index 523f542..fe8c8c8 100644 --- a/src/main/java/me/piitex/os/GitHubUtil.java +++ b/src/main/java/me/piitex/os/GitHubUtil.java @@ -1,11 +1,12 @@ package me.piitex.os; -import me.piitex.os.FileDownloader; import org.json.JSONArray; import org.json.JSONObject; import java.io.*; import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; /** @@ -30,8 +31,8 @@ public GitHubUtil(String repositoryUrl) { * @return {@link JSONObject} of the latest release. * @throws IOException If a connection cannot be made. */ - public JSONObject getLatestReleaseJson() throws IOException { - URL url = new URL(repositoryUrl + "releases/latest"); + public JSONObject getLatestReleaseJson() throws IOException, URISyntaxException { + URL url = new URI(repositoryUrl + "releases/latest").toURL(); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); @@ -51,13 +52,13 @@ public JSONObject getLatestReleaseJson() throws IOException { return null; } - public int getLatestReleaseID() throws IOException { + public int getLatestReleaseID() throws IOException, URISyntaxException { JSONObject object = getLatestReleaseJson(); return object.getInt("id"); } - public JSONArray getReleaseAssets(int releaseID) throws IOException { - URL url = new URL(repositoryUrl + "releases/" + releaseID + "/assets"); + public JSONArray getReleaseAssets(int releaseID) throws IOException, URISyntaxException { + URL url = new URI(repositoryUrl + "releases/" + releaseID + "/assets").toURL(); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); @@ -78,7 +79,7 @@ public JSONArray getReleaseAssets(int releaseID) throws IOException { } } - public JSONObject getReleaseAsset(int releaseID, String pattern) throws IOException { + public JSONObject getReleaseAsset(int releaseID, String pattern) throws IOException, URISyntaxException { JSONArray array = getReleaseAssets(releaseID); for (int i = 0; i < array.length(); i++) { JSONObject asset = array.getJSONObject(i); From 0b9bbc215121b568431b6594c8bc45e26f569512 Mon Sep 17 00:00:00 2001 From: piitex Date: Tue, 18 Nov 2025 09:01:17 -0600 Subject: [PATCH 02/78] Set connection timeout to 1000 milliseconds. --- src/main/java/me/piitex/os/GitHubUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/piitex/os/GitHubUtil.java b/src/main/java/me/piitex/os/GitHubUtil.java index fe8c8c8..a8fc652 100644 --- a/src/main/java/me/piitex/os/GitHubUtil.java +++ b/src/main/java/me/piitex/os/GitHubUtil.java @@ -34,7 +34,7 @@ public GitHubUtil(String repositoryUrl) { public JSONObject getLatestReleaseJson() throws IOException, URISyntaxException { URL url = new URI(repositoryUrl + "releases/latest").toURL(); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - + connection.setConnectTimeout(1000); connection.setRequestMethod("GET"); int responseCode = connection.getResponseCode(); From 829d5d957b4d1b28fa0f84f15118f0c638b3b0d5 Mon Sep 17 00:00:00 2001 From: piitex Date: Tue, 18 Nov 2025 09:01:42 -0600 Subject: [PATCH 03/78] Added DownloadListener callback when downloading an asset. --- src/main/java/me/piitex/os/GitHubUtil.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/piitex/os/GitHubUtil.java b/src/main/java/me/piitex/os/GitHubUtil.java index a8fc652..15f4f5d 100644 --- a/src/main/java/me/piitex/os/GitHubUtil.java +++ b/src/main/java/me/piitex/os/GitHubUtil.java @@ -90,8 +90,9 @@ public JSONObject getReleaseAsset(int releaseID, String pattern) throws IOExcept return null; } - public FileDownloader downloadAsset(int assetId, File output) throws IOException { + public FileDownloader downloadAsset(int assetId, File output, DownloadListener callback) { FileDownloader downloader = new FileDownloader(); + downloader.addDownloadListener(callback); downloader.addRequestProperty("Accept", "application/octet-stream"); downloader.startDownload(repositoryUrl + "releases/" + "assets/" + assetId, output); return downloader; From 6a69bd4c247255be97281e7274b0b06f48803d24 Mon Sep 17 00:00:00 2001 From: piitex Date: Tue, 18 Nov 2025 09:02:24 -0600 Subject: [PATCH 04/78] Added utilities for comparing software versions. --- src/main/java/me/piitex/os/Version.java | 91 +++++++++++++++++++++ src/main/java/me/piitex/os/VersionUtil.java | 35 ++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/main/java/me/piitex/os/Version.java create mode 100644 src/main/java/me/piitex/os/VersionUtil.java diff --git a/src/main/java/me/piitex/os/Version.java b/src/main/java/me/piitex/os/Version.java new file mode 100644 index 0000000..b743964 --- /dev/null +++ b/src/main/java/me/piitex/os/Version.java @@ -0,0 +1,91 @@ +package me.piitex.os; + +import org.jetbrains.annotations.NotNull; + +public class Version implements Comparable { + private final String prefix; + private final double version; + private final String suffix; + + /** + * Constructor for creating a version object. Used for comparing and updating third party software. + *

+ * Version Example: a1234b + *

+ * + * The comparison of versions works by the order of the letters and numbers. + * Letters will be sorted ascended by alphabetical order. A is 0 and B is 1. + * The higher the number the greater the version. The version `0` will be the seen as the earliest possible version. The version `a0a` is the same as `0`. + * + * @param prefix Optional prefix for the version. Example `a` + * @param version The version number as an Integer. Example `1234` + * @param suffix Optional suffix for the version. `Example b` + */ + public Version(String prefix, double version, String suffix) { + this.prefix = prefix; + this.version = version; + this.suffix = suffix; + } + + /** + * Constructor for creating a version object. Used for comparing and updating third party software. + *

+ * Version Example: 1234 + *

+ * + * The comparison of versions works by the order of the letters and numbers. + * Letters will be sorted ascended by alphabetical order. A is 0 and B is 1. + * The higher the number the greater the version. The version `0` will be the seen as the earliest possible version. The version `a0a` is the same as `0`. + * + * @param version The version number as an Integer. Example `1234` + */ + public Version(double version) { + this.prefix = ""; + this.version = version; + this.suffix = ""; + } + + public String getPrefix() { + return prefix; + } + + public double getVersion() { + return version; + } + + public String getSuffix() { + return suffix; + } + + public int getCalculatedVersion() { + int total = 0; + for (char c : getPrefix().toCharArray()) { + total += getCharInt(c); + } + total += version; + for (char c : getSuffix().toCharArray()) { + total += getCharInt(c); + } + return total; + } + + @Override + public int compareTo(@NotNull Object o) { + Version other = (Version) o; + + return getCalculatedVersion() - other.getCalculatedVersion(); + } + + private static int getCharInt(char c) { + char upperCaseLetter = Character.toUpperCase(c); + + // Check if the character is an English alphabet letter + if (upperCaseLetter >= 'A' && upperCaseLetter <= 'Z') { + // Calculate the zero-based index: 'A' maps to 0 + return upperCaseLetter - 'A'; + } else { + // Handle non-alphabet characters (e.g., space, numbers) + return 0; + } + } +} diff --git a/src/main/java/me/piitex/os/VersionUtil.java b/src/main/java/me/piitex/os/VersionUtil.java new file mode 100644 index 0000000..38c17b5 --- /dev/null +++ b/src/main/java/me/piitex/os/VersionUtil.java @@ -0,0 +1,35 @@ +package me.piitex.os; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class VersionUtil { + private static final Pattern VERSION_PATTERN = Pattern.compile("^([a-zA-Z]*)(\\d+)([a-zA-Z]*)$"); + + public static Version parseVersion(String version) { + if (version.contains("-")) { + version = version.replace("-", ""); // Dashes are not valid characters for versions. + // Example: project-1.0-snapshot -> project1.0snapshot + // Prefix: project + // Version: 1.0 + // Suffix: snapshot + } + + Matcher matcher = VERSION_PATTERN.matcher(version); + + if (matcher.matches()) { + // Group 1: Prefix (e.g., "v" from "v12a") + String prefix = matcher.group(1); + + // Group 2: Numerical Value (e.g., "12" from "v12.1.2a") + // We use Integer.parseInt() on the string captured by this group. + double numericalValue = Double.parseDouble(matcher.group(2)); + + // Group 3: Suffix (e.g., "a" from "v12a") + String suffix = matcher.group(3); + return new Version(prefix, numericalValue, suffix); + } else { + return null; + } + } +} From 7d3ec5679aa407ffb860a4dd3c4676c85a62a193 Mon Sep 17 00:00:00 2001 From: piitex Date: Tue, 18 Nov 2025 09:02:43 -0600 Subject: [PATCH 05/78] Added utility for unzipping files. --- src/main/java/me/piitex/os/ZipUtil.java | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/main/java/me/piitex/os/ZipUtil.java diff --git a/src/main/java/me/piitex/os/ZipUtil.java b/src/main/java/me/piitex/os/ZipUtil.java new file mode 100644 index 0000000..fa93df6 --- /dev/null +++ b/src/main/java/me/piitex/os/ZipUtil.java @@ -0,0 +1,41 @@ +package me.piitex.os; + +import java.io.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class ZipUtil { + + public static void unzipFile(File zip, File outDirectory) throws IOException { + if (!outDirectory.exists()) { + outDirectory.mkdir(); + } + try (ZipInputStream zipIn = new ZipInputStream(new BufferedInputStream(new FileInputStream(zip)))) { + ZipEntry entry = zipIn.getNextEntry(); + // iterates over entries in the zip file + while (entry != null) { + if (!entry.isDirectory()) { + // if the entry is a file, extract it + extractFile(entry.getName(), zipIn, outDirectory); + } else { + // if the entry is a directory, create the directory + File dir = new File(outDirectory, entry.getName()); + dir.mkdir(); + } + zipIn.closeEntry(); + entry = zipIn.getNextEntry(); + } + } + } + + private static void extractFile(String name, ZipInputStream zipIn, File output) throws IOException { + System.out.println("File name: " + name); + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(output, name)))) { + byte[] bytesIn = new byte[4096]; + int read; + while ((read = zipIn.read(bytesIn)) != -1) { + bos.write(bytesIn, 0, read); + } + } + } +} From d68f14c455cf10c3a68566402124cd0fed4732e8 Mon Sep 17 00:00:00 2001 From: piitex Date: Tue, 18 Nov 2025 09:02:56 -0600 Subject: [PATCH 06/78] Updated pom version. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9b0cf5e..6ea08a6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ me.piitex.engine ren-engine - 1.0.6-SNAPSHOT + 1.0.7-SNAPSHOT RenEngine From 76f8f281aa7668e5c4d60ee33df925d4f3f44427 Mon Sep 17 00:00:00 2001 From: piitex Date: Tue, 18 Nov 2025 09:09:07 -0600 Subject: [PATCH 07/78] Improved LimitedHasMap. --- .../me/piitex/engine/maps/LimitedHashMap.java | 80 +++---------------- 1 file changed, 12 insertions(+), 68 deletions(-) diff --git a/src/main/java/me/piitex/engine/maps/LimitedHashMap.java b/src/main/java/me/piitex/engine/maps/LimitedHashMap.java index 2ee38a2..e9946f2 100644 --- a/src/main/java/me/piitex/engine/maps/LimitedHashMap.java +++ b/src/main/java/me/piitex/engine/maps/LimitedHashMap.java @@ -1,88 +1,32 @@ package me.piitex.engine.maps; -import java.util.Collection; import java.util.HashMap; -import java.util.Map; -import java.util.Set; // A map with a limited size. This will also remove old entries when the limit is hit. -public class LimitedHashMap implements Map { - private final Map map; +public class LimitedHashMap extends HashMap { private final int limit; public LimitedHashMap(int limit) { - map = new HashMap<>(); this.limit = limit; } - @Override - public int size() { - return map.size(); - } - - @Override - public boolean isEmpty() { - return map.isEmpty(); - } - - @Override - public boolean containsKey(Object key) { - return map.containsKey(key); - } - - @Override - public boolean containsValue(Object value) { - return map.containsValue(value); - } - - @Override - public V get(Object key) { - return map.get(key); - } - @Override public V put(K key, V value) { - if (map.size() < limit) { - map.put(key, value); - } else { - map.entrySet().stream().findFirst().ifPresent(remove -> map.remove(remove.getKey())); + if (size() > limit) { + // Remove first entry not last + remove(keySet().stream().findFirst()); } + super.put(key, value); return value; } @Override - public V remove(Object key) { - return map.remove(key); - } - - @Override - public void putAll(Map m) { - m.forEach((k, v) -> { - if (size() < limit) { - map.put(k, v); - } else { - map.entrySet().stream().findFirst().ifPresent(remove -> map.remove(remove.getKey())); - } - }); - } - - @Override - public void clear() { - map.clear(); - } - - @Override - public Set keySet() { - return map.keySet(); - } - - @Override - public Collection values() { - return map.values(); - } - - @Override - public Set> entrySet() { - return map.entrySet(); + public V putIfAbsent(K key, V value) { + if (size() > limit) { + // Remove first entry not last + remove(keySet().stream().findFirst()); + } + super.putIfAbsent(key, value); + return value; } } From b44e825bf1d3cee6d5fa25150b2e9d963f980484 Mon Sep 17 00:00:00 2001 From: piitex Date: Tue, 18 Nov 2025 09:10:19 -0600 Subject: [PATCH 08/78] Window will now use the Container cached node if available. --- src/main/java/me/piitex/engine/Window.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/piitex/engine/Window.java b/src/main/java/me/piitex/engine/Window.java index 4f240a3..4c2e2e1 100644 --- a/src/main/java/me/piitex/engine/Window.java +++ b/src/main/java/me/piitex/engine/Window.java @@ -314,7 +314,12 @@ public void addContainer(Container container, int index) { containers.put(index, container); container.setWindow(this); // Store window reference. - Node assemble = container.assemble(); + Node assemble; + if (container.getNode() != null) { + assemble = container.getNode(); + } else { + assemble = container.assemble(); + } if (index > 0) { if (root.getChildren().size() < index) { From 622fd5fdac97cf2199c10cd4ccd552b93f50a882 Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 22 Nov 2025 11:17:55 -0600 Subject: [PATCH 09/78] Fixed issues with SliderOverlay. --- .../hanlders/events/SliderChangeEvent.java | 16 +++-- .../piitex/engine/overlays/SliderOverlay.java | 70 ++++++++----------- 2 files changed, 39 insertions(+), 47 deletions(-) diff --git a/src/main/java/me/piitex/engine/hanlders/events/SliderChangeEvent.java b/src/main/java/me/piitex/engine/hanlders/events/SliderChangeEvent.java index 0c2c3ba..6346fdf 100644 --- a/src/main/java/me/piitex/engine/hanlders/events/SliderChangeEvent.java +++ b/src/main/java/me/piitex/engine/hanlders/events/SliderChangeEvent.java @@ -4,22 +4,24 @@ public class SliderChangeEvent extends Event { private final SliderOverlay sliderOverlay; - private double value; + private final double newValue; + private final double oldValue; - public SliderChangeEvent(SliderOverlay sliderOverlay, double value) { + public SliderChangeEvent(SliderOverlay sliderOverlay, double newValue, double oldValue) { this.sliderOverlay = sliderOverlay; - this.value = value; + this.newValue = newValue; + this.oldValue = oldValue; } public SliderOverlay getSliderOverlay() { return sliderOverlay; } - public double getValue() { - return value; + public double getNewValue() { + return newValue; } - public void setValue(double value) { - this.value = value; + public double getOldValue() { + return oldValue; } } diff --git a/src/main/java/me/piitex/engine/overlays/SliderOverlay.java b/src/main/java/me/piitex/engine/overlays/SliderOverlay.java index 9442881..2faa7bd 100644 --- a/src/main/java/me/piitex/engine/overlays/SliderOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/SliderOverlay.java @@ -1,5 +1,6 @@ package me.piitex.engine.overlays; +import atlantafx.base.controls.ProgressSliderSkin; import javafx.scene.Node; import javafx.scene.control.Slider; import me.piitex.engine.hanlders.events.SliderChangeEvent; @@ -7,15 +8,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.net.MalformedURLException; - public class SliderOverlay extends Overlay implements Region { private final Slider slider; - private double width, height, prefWidth, prefHeight, maxWidth, maxHeight; private double maxValue, minValue, currentValue; private double blockIncrement; + private boolean tickLabels = false; + private ProgressSliderSkin sliderSkin; private ISliderChange sliderChange; + private double width, height, prefWidth, prefHeight, maxWidth, maxHeight; private static final Logger logger = LoggerFactory.getLogger(SliderOverlay.class); @@ -36,6 +36,10 @@ public SliderOverlay(double minValue, double maxValue, double currentValue, doub setHeight(height); } + public Slider getSlider() { + return slider; + } + public double getMaxValue() { return maxValue; } @@ -72,6 +76,22 @@ public void setBlockIncrement(double blockIncrement) { slider.setBlockIncrement(blockIncrement); } + public boolean isTickLabels() { + return tickLabels; + } + + public void setTickLabels(boolean tickLabels) { + this.tickLabels = tickLabels; + } + + public ProgressSliderSkin getSliderSkin() { + return sliderSkin; + } + + public void setSliderSkin(ProgressSliderSkin sliderSkin) { + this.sliderSkin = sliderSkin; + } + public ISliderChange getSliderChange() { return sliderChange; } @@ -82,45 +102,14 @@ public void onSliderMove(ISliderChange sliderChange) { @Override public Node render() { - if (getWidth() > 0) { - slider.setMinWidth(width); - } - - if (getHeight() > 0) { - slider.setMinHeight(height); - } - - if (getPrefWidth() > 0) { - slider.setPrefWidth(prefWidth); - } - - if (getPrefHeight() > 0) { - slider.setPrefHeight(prefHeight); - } - - if (getMaxWidth() > 0) { - slider.setMaxWidth(maxWidth); - } - - if (getMaxHeight() > 0) { - slider.setMaxHeight(maxHeight); - } slider.setTranslateX(getX()); slider.setTranslateY(getY()); slider.setBlockIncrement(blockIncrement); - // To design sliders we NEED a css file which contains the styling. I'm not able to inline this via code which sucks. - // Hopefully the slider gets improvements in JavaFX. - - // Check if they have css files - File sliderCss = new File(System.getProperty("user.dir") + "/game/css/slider.css"); - try { - slider.getStylesheets().add(sliderCss.toURI().toURL().toExternalForm()); - } catch (MalformedURLException e) { - logger.error("Could not css file for the slider.", e); - } - // Handle slider events - slider.setOnMouseDragged(event -> { - sliderChange.onSliderChange(new SliderChangeEvent(this, slider.getValue())); + slider.setSkin(new ProgressSliderSkin(slider)); + slider.valueProperty().addListener((_, oldValue, newValue) -> { + if (getSliderChange() != null) { + getSliderChange().onSliderChange(new SliderChangeEvent(this, newValue.doubleValue(), oldValue.doubleValue())); + } }); return slider; @@ -182,6 +171,7 @@ public double getMaxHeight() { @Override public void setMaxWidth(double w) { + this.maxWidth = w; slider.setMaxWidth(w); } From 12a42dc15901679d07d2b9959ba97ab3ce254df3 Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 22 Nov 2025 11:18:17 -0600 Subject: [PATCH 10/78] Fixed parsing issues with some versioning schemes. --- src/main/java/me/piitex/os/Version.java | 4 ++-- src/main/java/me/piitex/os/VersionUtil.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/me/piitex/os/Version.java b/src/main/java/me/piitex/os/Version.java index b743964..1b75adc 100644 --- a/src/main/java/me/piitex/os/Version.java +++ b/src/main/java/me/piitex/os/Version.java @@ -21,7 +21,7 @@ public class Version implements Comparable { * @param version The version number as an Integer. Example `1234` * @param suffix Optional suffix for the version. `Example b` */ - public Version(String prefix, double version, String suffix) { + public Version(String prefix, int version, String suffix) { this.prefix = prefix; this.version = version; this.suffix = suffix; @@ -39,7 +39,7 @@ public Version(String prefix, double version, String suffix) { * * @param version The version number as an Integer. Example `1234` */ - public Version(double version) { + public Version(int version) { this.prefix = ""; this.version = version; this.suffix = ""; diff --git a/src/main/java/me/piitex/os/VersionUtil.java b/src/main/java/me/piitex/os/VersionUtil.java index 38c17b5..194ed9b 100644 --- a/src/main/java/me/piitex/os/VersionUtil.java +++ b/src/main/java/me/piitex/os/VersionUtil.java @@ -14,6 +14,7 @@ public static Version parseVersion(String version) { // Version: 1.0 // Suffix: snapshot } + version = version.replace(".", ""); Matcher matcher = VERSION_PATTERN.matcher(version); @@ -23,7 +24,7 @@ public static Version parseVersion(String version) { // Group 2: Numerical Value (e.g., "12" from "v12.1.2a") // We use Integer.parseInt() on the string captured by this group. - double numericalValue = Double.parseDouble(matcher.group(2)); + int numericalValue = Integer.parseInt(matcher.group(2)); // Group 3: Suffix (e.g., "a" from "v12a") String suffix = matcher.group(3); From 9b901079b40942af5606b86181cbbeb7806c8aff Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Sun, 23 Nov 2025 15:43:09 -0600 Subject: [PATCH 11/78] Fixed container positioning issues. --- src/main/java/me/piitex/engine/containers/Container.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/me/piitex/engine/containers/Container.java b/src/main/java/me/piitex/engine/containers/Container.java index 058a633..8cb3be2 100644 --- a/src/main/java/me/piitex/engine/containers/Container.java +++ b/src/main/java/me/piitex/engine/containers/Container.java @@ -42,6 +42,8 @@ public Container(Node view, double x, double y, double width, double height) { setNode(view); this.x = x; this.y = y; + setX(x); + setY(y); setWidth(width); setHeight(height); } @@ -50,6 +52,8 @@ public Container(Node view, double x, double y, double width, double height, int setNode(view); this.x = x; this.y = y; + setX(x); + setY(y); setWidth(width); setHeight(height); setIndex(index); @@ -68,6 +72,7 @@ public double getX() { */ public void setX(double x) { this.x = x; + getNode().setTranslateX(x); } /** @@ -83,6 +88,7 @@ public double getY() { */ public void setY(double y) { this.y = y; + getNode().setTranslateY(y); } public IContainerClick getOnClick() { From 6f3edcce41f7dfc23f9c8b2c651e2bfa72a18b06 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Sun, 23 Nov 2025 15:43:30 -0600 Subject: [PATCH 12/78] Fixed pathing for some linux systems. --- src/main/java/me/piitex/os/OSPathing.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/me/piitex/os/OSPathing.java b/src/main/java/me/piitex/os/OSPathing.java index e40247b..2aa5c8f 100644 --- a/src/main/java/me/piitex/os/OSPathing.java +++ b/src/main/java/me/piitex/os/OSPathing.java @@ -10,11 +10,7 @@ public class OSPathing { public static File getDocumentsDirectory() { String userHome = System.getProperty("user.home"); - if (OSUtil.getOS().toLowerCase().contains("window")) { - return new File(userHome + File.separator + "Documents"); - } else { - return new File(userHome + File.separator + ".local" + File.separator + "share"); - } + return new File(userHome + File.separator + "Documents"); } public static File getAppDataDirectory() { @@ -28,7 +24,7 @@ public static File getAppDataDirectory() { } } - return new File(userHome + File.separator + ".local" + File.separator + "share"); + return new File(userHome + "/"); } public static File getHomeDirectory() { From cf8cb06c018229b1138419f887aaf490edc70cc7 Mon Sep 17 00:00:00 2001 From: piitex Date: Tue, 25 Nov 2025 20:31:17 -0600 Subject: [PATCH 13/78] Linux appdata will be pathed to .var --- src/main/java/me/piitex/os/OSPathing.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/me/piitex/os/OSPathing.java b/src/main/java/me/piitex/os/OSPathing.java index 2aa5c8f..8413637 100644 --- a/src/main/java/me/piitex/os/OSPathing.java +++ b/src/main/java/me/piitex/os/OSPathing.java @@ -8,6 +8,8 @@ public class OSPathing { private static final Logger logger = LoggerFactory.getLogger(OSPathing.class); + public static String groupId = "me.piitex.app.App"; // Only used for linux + public static File getDocumentsDirectory() { String userHome = System.getProperty("user.home"); return new File(userHome + File.separator + "Documents"); @@ -22,6 +24,9 @@ public static File getAppDataDirectory() { if (localAppData != null) { return new File(localAppData); } + } else if (os.contains("Linux")) { + validateAppData(); + return new File(userHome + "/.var/app/" + groupId + "/"); } return new File(userHome + "/"); @@ -59,4 +64,10 @@ public static void initiateProjectDirectories(String project, boolean documents, } } + private static void validateAppData() { + if (OSUtil.getOS().contains("Linux") && groupId.equals("me.piitex.app.App")) { + throw new RuntimeException("You must set the linux group id for the application! OSPathing.groupId = \"your.group.app\""); + } + } + } From 19e96bf6e412845f66dac1660518646ac21dcb79 Mon Sep 17 00:00:00 2001 From: piitex Date: Wed, 26 Nov 2025 19:12:33 -0600 Subject: [PATCH 14/78] Added support Linux support for ProcessUtil --- src/main/java/me/piitex/os/ProcessUtil.java | 59 ++++++++++++++++----- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/src/main/java/me/piitex/os/ProcessUtil.java b/src/main/java/me/piitex/os/ProcessUtil.java index 96644c0..fd886c0 100644 --- a/src/main/java/me/piitex/os/ProcessUtil.java +++ b/src/main/java/me/piitex/os/ProcessUtil.java @@ -7,12 +7,15 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Optional; public class ProcessUtil { private static final Logger logger = LoggerFactory.getLogger(ProcessUtil.class); - private static final boolean VALIDATE_OS = OSUtil.getOS().toLowerCase().contains("window"); + private static final boolean VALIDATE_OS = OSUtil.getOS().toLowerCase().contains("window") || OSUtil.getOS().toLowerCase().contains("linux"); static { if (!VALIDATE_OS) { @@ -26,22 +29,48 @@ public static boolean isProcessRunning(long pid) { } try { - ProcessBuilder processBuilder = new ProcessBuilder( - "tasklist", - "/FI", - "PID eq " + pid - ); + // Define the command based on the operating system + List command = new ArrayList<>(); + if (OSUtil.getOS().contains("Windows")) { + // Command: tasklist /FI "PID eq [pid]" + command = Arrays.asList( + "tasklist", + "/FI", + "PID eq " + pid + ); + } else if (OSUtil.getOS().contains("Linux")) { + command = Arrays.asList( + "ps", + "-p", + String.valueOf(pid) + ); + } + + ProcessBuilder processBuilder = new ProcessBuilder(command); Process process = processBuilder.start(); - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - String line; - while ((line = reader.readLine()) != null) { - if (line.contains(" " + pid + " ")) { // Check for PID in the output - return true; + + if (OSUtil.getOS().contains("Windows")) { + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + if (line.contains(" " + pid + " ") || line.startsWith(String.valueOf(pid))) { + return true; + } + } + return false; + } else if (OSUtil.getOS().contains("Linux")) { + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + int lineCount = 0; + while (reader.readLine() != null) { + lineCount++; } + return lineCount > 1; + } else { + return false; } - return false; } catch (IOException e) { - logger.error("Unable to check process condition!", e); + logger.error("Unable to check process condition for PID {}", pid, e); + Thread.currentThread().interrupt(); // Restore interrupted status return false; } } @@ -76,4 +105,8 @@ public static boolean killProcess(long pid) { Optional processHandle = getRunningProcess(pid); return processHandle.map(ProcessHandle::destroyForcibly).orElse(false); } + + public static boolean isValidOS() { + return VALIDATE_OS; + } } From b4caf2da00572abfe3fbd97f61c66479cd34015e Mon Sep 17 00:00:00 2001 From: piitex Date: Wed, 26 Nov 2025 19:13:40 -0600 Subject: [PATCH 15/78] Appdata directory will now create if it doesn't exist. --- src/main/java/me/piitex/os/OSPathing.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/me/piitex/os/OSPathing.java b/src/main/java/me/piitex/os/OSPathing.java index 8413637..9c4c614 100644 --- a/src/main/java/me/piitex/os/OSPathing.java +++ b/src/main/java/me/piitex/os/OSPathing.java @@ -4,6 +4,7 @@ import org.slf4j.LoggerFactory; import java.io.File; +import java.io.IOException; public class OSPathing { private static final Logger logger = LoggerFactory.getLogger(OSPathing.class); @@ -26,6 +27,16 @@ public static File getAppDataDirectory() { } } else if (os.contains("Linux")) { validateAppData(); + File file = new File(userHome + "/.var/app/" + groupId + "/"); + if (!file.exists()) { + try { + if (file.createNewFile()) { + logger.info("Created appdata directory..."); + } + } catch (IOException e) { + logger.error("Could not create appdata folder!", e); + } + } return new File(userHome + "/.var/app/" + groupId + "/"); } From 5713146370f9e2d4e533b9129d463c450bc1c918 Mon Sep 17 00:00:00 2001 From: piitex Date: Wed, 26 Nov 2025 22:01:08 -0600 Subject: [PATCH 16/78] Added function to create zip files. --- src/main/java/me/piitex/os/ZipUtil.java | 63 ++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/piitex/os/ZipUtil.java b/src/main/java/me/piitex/os/ZipUtil.java index fa93df6..3ea8035 100644 --- a/src/main/java/me/piitex/os/ZipUtil.java +++ b/src/main/java/me/piitex/os/ZipUtil.java @@ -1,10 +1,15 @@ package me.piitex.os; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; public class ZipUtil { + private static final Logger logger = LoggerFactory.getLogger(ZipUtil.class); public static void unzipFile(File zip, File outDirectory) throws IOException { if (!outDirectory.exists()) { @@ -12,6 +17,7 @@ public static void unzipFile(File zip, File outDirectory) throws IOException { } try (ZipInputStream zipIn = new ZipInputStream(new BufferedInputStream(new FileInputStream(zip)))) { ZipEntry entry = zipIn.getNextEntry(); + System.out.println("Entry: " + entry); // iterates over entries in the zip file while (entry != null) { if (!entry.isDirectory()) { @@ -20,6 +26,7 @@ public static void unzipFile(File zip, File outDirectory) throws IOException { } else { // if the entry is a directory, create the directory File dir = new File(outDirectory, entry.getName()); + System.out.println("Making Directory: " + dir); dir.mkdir(); } zipIn.closeEntry(); @@ -29,8 +36,11 @@ public static void unzipFile(File zip, File outDirectory) throws IOException { } private static void extractFile(String name, ZipInputStream zipIn, File output) throws IOException { + System.out.println("Output: " + output.getAbsolutePath()); System.out.println("File name: " + name); - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(output, name)))) { + File newFile = new File(output, name); + newFile.getParentFile().mkdirs(); + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFile))) { byte[] bytesIn = new byte[4096]; int read; while ((read = zipIn.read(bytesIn)) != -1) { @@ -38,4 +48,55 @@ private static void extractFile(String name, ZipInputStream zipIn, File output) } } } + + public static void zipDirectory(File zipFile, File directory) { + if (!directory.isDirectory()) { + return; + } + + if (zipFile.exists()) { + throw new RuntimeException("Zip file already exists! Cannot create."); + } + + try { + if (zipFile.createNewFile()) { + logger.info("Created zip file '{}'", zipFile.getAbsolutePath()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + try { + ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(zipFile)); + + int zipped = 0; + for (File file : directory.listFiles()) { + if (!file.isFile()) { + continue; + } + zipFile(file, outputStream, zipped); + } + + outputStream.close(); + logger.info("Zipped '{}' files", zipped); + } catch (IOException e) { + logger.error("Could not create zip file!", e); + } + + } + + private static void zipFile(File file, OutputStream outputStream, int processed) { + byte[] buffer = new byte[1024]; + int bytesRead; + try (FileInputStream fileInputStream = new FileInputStream(file)) { + + // 3. Read the file content in chunks and write to the ZipOutputStream + while ((bytesRead = fileInputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, bytesRead); + processed++; + } + } catch (IOException e) { + logger.error("Could not zip '{}'", file.getAbsolutePath(), e); + } + } } From e5e653eea84f2a0a5d487d4c48706b99cf60637d Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 29 Nov 2025 12:47:53 -0600 Subject: [PATCH 17/78] Added setMaxSize for consistency. --- .../me/piitex/engine/overlays/BoxOverlay.java | 8 +++++ .../piitex/engine/overlays/ButtonBuilder.java | 23 ++++++++++++ .../piitex/engine/overlays/ButtonOverlay.java | 28 +++++++++++++++ .../engine/overlays/ChoiceBoxOverlay.java | 7 ++++ .../engine/overlays/ColorPickerOverlay.java | 7 ++++ .../engine/overlays/ComboBoxOverlay.java | 7 ++++ .../engine/overlays/MessageOverlay.java | 7 ++++ .../engine/overlays/NotificationOverlay.java | 7 ++++ .../engine/overlays/PasswordFieldOverlay.java | 7 ++++ .../engine/overlays/ProgressBarOverlay.java | 7 ++++ .../me/piitex/engine/overlays/Region.java | 1 + .../engine/overlays/RichTextAreaOverlay.java | 7 ++++ .../engine/overlays/SeparatorOverlay.java | 7 ++++ .../piitex/engine/overlays/SliderOverlay.java | 7 ++++ .../engine/overlays/SpinnerNumberOverlay.java | 7 ++++ .../engine/overlays/TextAreaOverlay.java | 7 ++++ ...ieldOverlay.java => TextFieldOverlay.java} | 35 +++++++++++++------ .../engine/overlays/TextFlowOverlay.java | 9 ++++- 18 files changed, 176 insertions(+), 12 deletions(-) rename src/main/java/me/piitex/engine/overlays/{InputFieldOverlay.java => TextFieldOverlay.java} (82%) diff --git a/src/main/java/me/piitex/engine/overlays/BoxOverlay.java b/src/main/java/me/piitex/engine/overlays/BoxOverlay.java index ffb62f9..bb571e0 100644 --- a/src/main/java/me/piitex/engine/overlays/BoxOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/BoxOverlay.java @@ -117,6 +117,14 @@ public void setMaxHeight(double h) { this.maxHeight = h; } + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + rectangle.setWidth(w); + rectangle.setHeight(h); + } + public Rectangle getRectangle() { return rectangle; } diff --git a/src/main/java/me/piitex/engine/overlays/ButtonBuilder.java b/src/main/java/me/piitex/engine/overlays/ButtonBuilder.java index 2775fd6..9dc3578 100644 --- a/src/main/java/me/piitex/engine/overlays/ButtonBuilder.java +++ b/src/main/java/me/piitex/engine/overlays/ButtonBuilder.java @@ -1,21 +1,26 @@ package me.piitex.engine.overlays; import javafx.scene.paint.Paint; +import me.piitex.engine.Element; import me.piitex.engine.loaders.FontLoader; import org.kordamp.ikonli.javafx.FontIcon; +import java.util.ArrayList; import java.util.LinkedList; +import java.util.List; public class ButtonBuilder { private final String id; private String text; private FontIcon icon; private FontLoader font; + private Element graphic; private Paint textFill; private double width, height, prefHeight, prefWidth, maxWidth, maxHeight; private double x, y; private boolean enabled = true; private final LinkedList images = new LinkedList<>(); + private final List styles = new ArrayList<>(); /** * Initializes the builder with the mandatory button ID. @@ -147,6 +152,11 @@ public ButtonBuilder addImage(ImageOverlay image) { return this; } + public ButtonBuilder addStyle(String style) { + this.styles.add(style); + return this; + } + /** * Sets the enabled state of the button. * @param enabled True to enable the button, false to disable. @@ -157,6 +167,11 @@ public ButtonBuilder setEnabled(boolean enabled) { return this; } + public ButtonBuilder setGraphic(Element graphic) { + this.graphic = graphic; + return this; + } + /** * Builds and returns a new ButtonOverlay object with the configured properties. * @return A new ButtonOverlay instance. @@ -181,6 +196,10 @@ public FontLoader getFont() { return font; } + public Element getGraphic() { + return graphic; + } + public Paint getTextFill() { return textFill; } @@ -225,4 +244,8 @@ public boolean isEnabled() { public LinkedList getImages() { return images; } + + public List getStyles() { + return styles; + } } \ No newline at end of file diff --git a/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java b/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java index 7afd73e..6a65a05 100644 --- a/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java @@ -6,6 +6,7 @@ import javafx.scene.image.ImageView; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; +import me.piitex.engine.Element; import me.piitex.engine.loaders.FontLoader; import org.kordamp.ikonli.javafx.FontIcon; @@ -16,9 +17,11 @@ public class ButtonOverlay extends Overlay implements Region { private final String id; private String text; private FontLoader font; + private Element graphic; private final FontIcon icon; private final LinkedList images = new LinkedList<>(); private Paint textFill; + private Pos alignment; private double width, height, prefHeight, prefWidth, maxWidth, maxHeight; @@ -31,6 +34,7 @@ protected ButtonOverlay(ButtonBuilder builder) { this.text = builder.getText(); this.icon = builder.getIcon(); this.font = builder.getFont(); + this.graphic = builder.getGraphic(); this.textFill = builder.getTextFill(); this.width = builder.getWidth(); this.height = builder.getHeight(); @@ -39,6 +43,7 @@ protected ButtonOverlay(ButtonBuilder builder) { this.maxWidth = builder.getMaxWidth(); this.maxHeight = builder.getMaxHeight(); this.images.addAll(builder.getImages()); + builder.getStyles().forEach(this::addStyle); setX(builder.getX()); setY(builder.getY()); this.button = new Button(); @@ -72,6 +77,15 @@ public void setFont(FontLoader font) { button.setFont(font.getFont()); } + public Pos getAlignment() { + return alignment; + } + + public void setAlignment(Pos alignment) { + this.alignment = alignment; + button.setAlignment(alignment); + } + @Override public Node render() { button.setId(id); @@ -101,6 +115,7 @@ public Node render() { } } } + if (icon != null) { button.setGraphic(icon); } @@ -113,6 +128,12 @@ public Node render() { if (textFill != null) { button.setTextFill(textFill); } + if (graphic != null) { + button.setGraphic(graphic.assemble()); + } + if (alignment != null) { + button.setAlignment(alignment); + } if (height > 0) { button.setMaxHeight(height); button.setPrefHeight(height); @@ -199,6 +220,13 @@ public void setMaxHeight(double h) { button.setMaxHeight(h); } + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + button.setMaxSize(w, h); + } + public LinkedList getImages() { return images; } diff --git a/src/main/java/me/piitex/engine/overlays/ChoiceBoxOverlay.java b/src/main/java/me/piitex/engine/overlays/ChoiceBoxOverlay.java index 4e9e5b9..f213868 100644 --- a/src/main/java/me/piitex/engine/overlays/ChoiceBoxOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/ChoiceBoxOverlay.java @@ -193,4 +193,11 @@ public void setMaxHeight(double h) { this.maxHeight = h; choiceBox.setMaxHeight(h); } + + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + choiceBox.setMaxSize(w, h); + } } diff --git a/src/main/java/me/piitex/engine/overlays/ColorPickerOverlay.java b/src/main/java/me/piitex/engine/overlays/ColorPickerOverlay.java index 2c913e6..0b50bfd 100644 --- a/src/main/java/me/piitex/engine/overlays/ColorPickerOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/ColorPickerOverlay.java @@ -116,4 +116,11 @@ public void setMaxHeight(double h) { colorPicker.setMaxHeight(h); } + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + colorPicker.setMaxSize(w, h); + } + } diff --git a/src/main/java/me/piitex/engine/overlays/ComboBoxOverlay.java b/src/main/java/me/piitex/engine/overlays/ComboBoxOverlay.java index b9642b1..666873d 100644 --- a/src/main/java/me/piitex/engine/overlays/ComboBoxOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/ComboBoxOverlay.java @@ -183,4 +183,11 @@ public void setMaxHeight(double h) { this.maxHeight = h; comboBox.setMaxHeight(h); } + + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + comboBox.setMaxSize(w, h); + } } diff --git a/src/main/java/me/piitex/engine/overlays/MessageOverlay.java b/src/main/java/me/piitex/engine/overlays/MessageOverlay.java index 5a6d249..18fe959 100644 --- a/src/main/java/me/piitex/engine/overlays/MessageOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/MessageOverlay.java @@ -217,4 +217,11 @@ public void setMaxHeight(double h) { this.maxHeight = h; atlantafxMessage.setMaxHeight(h); } + + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + atlantafxMessage.setMaxSize(w, h); + } } \ No newline at end of file diff --git a/src/main/java/me/piitex/engine/overlays/NotificationOverlay.java b/src/main/java/me/piitex/engine/overlays/NotificationOverlay.java index 19071e5..df9be66 100644 --- a/src/main/java/me/piitex/engine/overlays/NotificationOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/NotificationOverlay.java @@ -137,4 +137,11 @@ public void setMaxHeight(double h) { notification.setMaxHeight(h); } + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + notification.setMaxSize(w, h); + } + } \ No newline at end of file diff --git a/src/main/java/me/piitex/engine/overlays/PasswordFieldOverlay.java b/src/main/java/me/piitex/engine/overlays/PasswordFieldOverlay.java index a2affb0..af2972f 100644 --- a/src/main/java/me/piitex/engine/overlays/PasswordFieldOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/PasswordFieldOverlay.java @@ -147,4 +147,11 @@ public void setMaxHeight(double h) { this.maxHeight = h; textField.setMaxHeight(h); } + + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + textField.setMaxSize(w, h); + } } diff --git a/src/main/java/me/piitex/engine/overlays/ProgressBarOverlay.java b/src/main/java/me/piitex/engine/overlays/ProgressBarOverlay.java index fc72ef8..a993b45 100644 --- a/src/main/java/me/piitex/engine/overlays/ProgressBarOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/ProgressBarOverlay.java @@ -107,4 +107,11 @@ public void setMaxHeight(double h) { this.maxHeight = h; progressBar.setMaxHeight(h); } + + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + progressBar.setMaxSize(w, h); + } } diff --git a/src/main/java/me/piitex/engine/overlays/Region.java b/src/main/java/me/piitex/engine/overlays/Region.java index 27fad2e..aba04bc 100644 --- a/src/main/java/me/piitex/engine/overlays/Region.java +++ b/src/main/java/me/piitex/engine/overlays/Region.java @@ -21,4 +21,5 @@ public interface Region { void setMaxWidth(double w); void setMaxHeight(double h); + void setMaxSize(double w, double h); } diff --git a/src/main/java/me/piitex/engine/overlays/RichTextAreaOverlay.java b/src/main/java/me/piitex/engine/overlays/RichTextAreaOverlay.java index 2940721..3fdae77 100644 --- a/src/main/java/me/piitex/engine/overlays/RichTextAreaOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/RichTextAreaOverlay.java @@ -480,4 +480,11 @@ public void setMaxHeight(double h) { this.maxHeight = h; textArea.setMaxHeight(h); } + + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + textArea.setMaxSize(w, h); + } } diff --git a/src/main/java/me/piitex/engine/overlays/SeparatorOverlay.java b/src/main/java/me/piitex/engine/overlays/SeparatorOverlay.java index 87a784f..1262917 100644 --- a/src/main/java/me/piitex/engine/overlays/SeparatorOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/SeparatorOverlay.java @@ -90,6 +90,13 @@ public void setMaxHeight(double h) { separator.setMaxHeight(h); } + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + separator.setMaxSize(w, h); + } + @Override public Node render() { separator.getStyleClass().addAll(getStyles()); diff --git a/src/main/java/me/piitex/engine/overlays/SliderOverlay.java b/src/main/java/me/piitex/engine/overlays/SliderOverlay.java index 2faa7bd..96ac019 100644 --- a/src/main/java/me/piitex/engine/overlays/SliderOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/SliderOverlay.java @@ -180,4 +180,11 @@ public void setMaxHeight(double h) { this.maxHeight = h; slider.setMaxHeight(h); } + + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + slider.setMaxSize(w, h); + } } diff --git a/src/main/java/me/piitex/engine/overlays/SpinnerNumberOverlay.java b/src/main/java/me/piitex/engine/overlays/SpinnerNumberOverlay.java index 997c228..1b165dc 100644 --- a/src/main/java/me/piitex/engine/overlays/SpinnerNumberOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/SpinnerNumberOverlay.java @@ -156,4 +156,11 @@ public void setMaxHeight(double h) { this.maxHeight = h; spinner.setMaxHeight(h); } + + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + spinner.setMaxSize(w, h); + } } diff --git a/src/main/java/me/piitex/engine/overlays/TextAreaOverlay.java b/src/main/java/me/piitex/engine/overlays/TextAreaOverlay.java index 92ae38e..c887cbc 100644 --- a/src/main/java/me/piitex/engine/overlays/TextAreaOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/TextAreaOverlay.java @@ -175,6 +175,13 @@ public void setMaxHeight(double h) { textArea.setMaxHeight(h); } + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + textArea.setMaxSize(w, h); + } + public IInputSetEvent getiInputSetEvent() { return iInputSetEvent; } diff --git a/src/main/java/me/piitex/engine/overlays/InputFieldOverlay.java b/src/main/java/me/piitex/engine/overlays/TextFieldOverlay.java similarity index 82% rename from src/main/java/me/piitex/engine/overlays/InputFieldOverlay.java rename to src/main/java/me/piitex/engine/overlays/TextFieldOverlay.java index 2da2c3f..152848b 100644 --- a/src/main/java/me/piitex/engine/overlays/InputFieldOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/TextFieldOverlay.java @@ -7,27 +7,29 @@ import me.piitex.engine.loaders.FontLoader; import me.piitex.engine.overlays.events.IInputSetEvent; -public class InputFieldOverlay extends Overlay implements Region { +public class TextFieldOverlay extends Overlay implements Region { private final TextField textField; private double width, height, prefWidth, prefHeight, maxWidth, maxHeight; - private double scaleWidth, scaleHeight; private final String defaultInput; private FontLoader fontLoader; private String hintText = ""; private IInputSetEvent iInputSetEvent; - public InputFieldOverlay(String defaultInput, double x, double y, double width, double height) { - this.textField = new TextField(); - this.defaultInput = defaultInput; - this.width = width; - this.height = height; - setNode(textField); - setX(x); - setY(y); + public TextFieldOverlay(String defaultInput, String hintText) { + this(defaultInput, hintText, 0, 0, 0, 0); + } + + + public TextFieldOverlay(String defaultInput, String hintText, double width, double height) { + this(defaultInput, hintText, 0, 0, width, height); } - public InputFieldOverlay(String defaultInput, String hintText, double x, double y, double width, double height) { + public TextFieldOverlay(String defaultInput, double x, double y, double width, double height) { + this(defaultInput, "", x, y, width, height); + } + + public TextFieldOverlay(String defaultInput, String hintText, double x, double y, double width, double height) { this.textField = new TextField(); this.defaultInput = defaultInput; this.hintText = hintText; @@ -163,6 +165,13 @@ public void setMaxHeight(double h) { textField.setMaxHeight(h); } + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + textField.setMaxSize(w, h); + } + public IInputSetEvent getiInputSetEvent() { return iInputSetEvent; } @@ -170,4 +179,8 @@ public IInputSetEvent getiInputSetEvent() { public void onInputSetEvent(IInputSetEvent iInputSetEvent) { this.iInputSetEvent = iInputSetEvent; } + + public TextField getTextField() { + return textField; + } } diff --git a/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java b/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java index ca0d8ff..88be327 100644 --- a/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java @@ -162,7 +162,7 @@ public Node render() { button.setTextFill(textFillColor); } } - case InputFieldOverlay inputField -> { + case TextFieldOverlay inputField -> { if (font != null) { inputField.setFont(font); } @@ -296,4 +296,11 @@ public void setMaxHeight(double h) { this.maxHeight = h; textFlow.setMaxHeight(h); } + + @Override + public void setMaxSize(double w, double h) { + this.maxWidth = w; + this.maxHeight = h; + textFlow.setMaxSize(w, h); + } } From 08e95a425ec981a294a5b8d7d6187640e3e65286 Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 29 Nov 2025 12:48:08 -0600 Subject: [PATCH 18/78] Removed scaleY and scaleX. --- .../me/piitex/engine/overlays/Overlay.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/main/java/me/piitex/engine/overlays/Overlay.java b/src/main/java/me/piitex/engine/overlays/Overlay.java index d832a67..61dd296 100644 --- a/src/main/java/me/piitex/engine/overlays/Overlay.java +++ b/src/main/java/me/piitex/engine/overlays/Overlay.java @@ -58,9 +58,7 @@ */ public abstract class Overlay extends Element { private double x,y; - private double scaleX, scaleY; private String tooltip; - private IOverlayHover iOverlayHover; private IOverlayHoverExit iOverlayHoverExit; private IOverlayClick iOverlayClick; @@ -107,22 +105,6 @@ public void setTooltip(String tooltip) { this.tooltip = tooltip; } - public double getScaleX() { - return scaleX; - } - - public void setScaleX(double scaleX) { - this.scaleX = scaleX; - } - - public double getScaleY() { - return scaleY; - } - - public void setScaleY(double scaleY) { - this.scaleY = scaleY; - } - public void onClick(IOverlayClick iOverlayClick) { this.iOverlayClick = iOverlayClick; } From 671f0770cf45f94fb00c916197d06d886364e265 Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 29 Nov 2025 12:48:28 -0600 Subject: [PATCH 19/78] Added IconOverlay. --- .../piitex/engine/overlays/IconOverlay.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/main/java/me/piitex/engine/overlays/IconOverlay.java diff --git a/src/main/java/me/piitex/engine/overlays/IconOverlay.java b/src/main/java/me/piitex/engine/overlays/IconOverlay.java new file mode 100644 index 0000000..93d0e3f --- /dev/null +++ b/src/main/java/me/piitex/engine/overlays/IconOverlay.java @@ -0,0 +1,74 @@ +package me.piitex.engine.overlays; + +import javafx.scene.Node; +import javafx.scene.paint.Paint; +import org.kordamp.ikonli.Ikon; +import org.kordamp.ikonli.javafx.FontIcon; + +/** + * RenEngine wrapper for {@link FontIcon}. Style classes are disabled for this overlay. + */ +public class IconOverlay extends Overlay { + private final FontIcon fontIcon; + private String iconCode; + private int iconSize = 16; + private Paint color; + + public IconOverlay() { + this.fontIcon = new FontIcon(); + setNode(fontIcon); + } + + public IconOverlay(Ikon ikon) { + this.fontIcon = new FontIcon(ikon); + setNode(fontIcon); + } + + public IconOverlay(String iconCode) { + this.fontIcon = new FontIcon(iconCode); + this.iconCode = iconCode; + setNode(fontIcon); + } + + public void setIconCode(String iconCode) { + this.iconCode = iconCode; + fontIcon.setIconLiteral(iconCode); + } + + public int getIconSize() { + return iconSize; + } + + public void setIconSize(int iconSize) { + this.iconSize = iconSize; + fontIcon.setIconSize(iconSize); + } + + public Paint getColor() { + return color; + } + + public void setColor(Paint color) { + this.color = color; + fontIcon.setIconColor(color); + } + + @Override + protected Node render() { + fontIcon.getStyleClass().clear(); + fontIcon.setTranslateX(getX()); + fontIcon.setTranslateY(getY()); + if (iconCode != null) { + fontIcon.setIconLiteral(iconCode); + } + fontIcon.setIconSize(iconSize); + if (color != null) { + fontIcon.setIconColor(color); + } + return fontIcon; + } + + public FontIcon getFontIcon() { + return fontIcon; + } +} \ No newline at end of file From 46cd91813c668833e66705cea2b5600bcc039e1a Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 29 Nov 2025 12:48:58 -0600 Subject: [PATCH 20/78] Added copy function for InfoFile. --- src/main/java/me/piitex/os/configurations/InfoFile.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/me/piitex/os/configurations/InfoFile.java b/src/main/java/me/piitex/os/configurations/InfoFile.java index edab1c8..f1d81d0 100644 --- a/src/main/java/me/piitex/os/configurations/InfoFile.java +++ b/src/main/java/me/piitex/os/configurations/InfoFile.java @@ -433,4 +433,13 @@ public Map getEntryMap() { public void setEntryMap(Map entryMap) { this.entryMap = entryMap; } + + public static InfoFile copy(InfoFile input) { + InfoFile infoFile = new InfoFile(); + + input.getEntryMap().forEach((s, s2) -> infoFile.getEntryMap().put(s, s2)); + infoFile.update(); + + return infoFile; + } } From 0c37599ec74a5783299e5cef07be8aa808d3f10a Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 29 Nov 2025 22:04:56 -0600 Subject: [PATCH 21/78] Fixed scrolling conditions not being applied. --- .../piitex/engine/containers/ScrollContainer.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/me/piitex/engine/containers/ScrollContainer.java b/src/main/java/me/piitex/engine/containers/ScrollContainer.java index 0f386ed..25740b5 100644 --- a/src/main/java/me/piitex/engine/containers/ScrollContainer.java +++ b/src/main/java/me/piitex/engine/containers/ScrollContainer.java @@ -18,6 +18,10 @@ public class ScrollContainer extends Container { private boolean pannable = false; private double scrollPosition; + public ScrollContainer(Layout layout, double width, double height) { + this(layout, 0, 0, width, height); + } + public ScrollContainer(Layout layout, double x, double y, double width, double height) { ScrollPane tempPane = new ScrollPane(); this.scrollPane = tempPane; @@ -47,6 +51,11 @@ public boolean isHorizontalScroll() { public void setHorizontalScroll(boolean horizontalScroll) { this.horizontalScroll = horizontalScroll; + if (!horizontalScroll && !scrollWhenNeeded) { + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + } else if (horizontalScroll && !scrollWhenNeeded) { + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + } } public boolean isVerticalScroll() { @@ -55,6 +64,11 @@ public boolean isVerticalScroll() { public void setVerticalScroll(boolean verticalScroll) { this.verticalScroll = verticalScroll; + if (!verticalScroll && !scrollWhenNeeded) { + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + } else if (verticalScroll && !scrollWhenNeeded) { + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + } } public boolean isScrollWhenNeeded() { From f9ab2e04cebf0ac5c43d4a5527cac93a6db70f0a Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 29 Nov 2025 22:05:22 -0600 Subject: [PATCH 22/78] Views can now hold special properties and data. --- src/main/java/me/piitex/engine/Renderer.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/me/piitex/engine/Renderer.java b/src/main/java/me/piitex/engine/Renderer.java index 10593c6..2c32b88 100644 --- a/src/main/java/me/piitex/engine/Renderer.java +++ b/src/main/java/me/piitex/engine/Renderer.java @@ -31,6 +31,8 @@ public class Renderer extends Element { private final List styles = new ArrayList<>(); private Window window; + private final Properties properties = new Properties(); + // Handlers for events private IRendererKey iRendererKey; @@ -384,4 +386,16 @@ public Node assemble() { return node; } + + public void addProperties(String key, String data) { + properties.setProperty(key, data); + } + + public String getProperty(String key) { + return properties.getProperty(key); + } + + public boolean hasProperty(String key) { + return properties.containsKey(key); + } } From 1ede0fc960e53b69e91cff98252a233cb0257aff Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 29 Nov 2025 22:05:38 -0600 Subject: [PATCH 23/78] Improved version examples. --- src/main/java/me/piitex/os/Version.java | 38 +++++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/main/java/me/piitex/os/Version.java b/src/main/java/me/piitex/os/Version.java index 1b75adc..e32f60e 100644 --- a/src/main/java/me/piitex/os/Version.java +++ b/src/main/java/me/piitex/os/Version.java @@ -2,7 +2,7 @@ import org.jetbrains.annotations.NotNull; -public class Version implements Comparable { +public class Version implements Comparable { private final String prefix; private final double version; private final String suffix; @@ -10,12 +10,26 @@ public class Version implements Comparable { /** * Constructor for creating a version object. Used for comparing and updating third party software. *

- * Version Example: a1234b + * Version Examples" + *

    + * 1.0 + *
+ *
    + * v1.0 + *
+ *
    + * 1.0-SNAPSHOT + *
+ *
    + * v1.0-SNAPSHOT + *
*

* * The comparison of versions works by the order of the letters and numbers. * Letters will be sorted ascended by alphabetical order. A is 0 and B is 1. * The higher the number the greater the version. The version `0` will be the seen as the earliest possible version. The version `a0a` is the same as `0`. + *

+ * Versions schemes cannot change for the comparison. v1.0 /= 1.0-SNAPSHOT /= 1.0. Any changes made to versions schemes must be validated before parsing. * * @param prefix Optional prefix for the version. Example `a` * @param version The version number as an Integer. Example `1234` @@ -30,12 +44,26 @@ public Version(String prefix, int version, String suffix) { /** * Constructor for creating a version object. Used for comparing and updating third party software. *

- * Version Example: 1234 + * Version Examples" + *

    + * 1.0 + *
+ *
    + * v1.0 + *
+ *
    + * 1.0-SNAPSHOT + *
+ *
    + * v1.0-SNAPSHOT + *
*

* * The comparison of versions works by the order of the letters and numbers. * Letters will be sorted ascended by alphabetical order. A is 0 and B is 1. * The higher the number the greater the version. The version `0` will be the seen as the earliest possible version. The version `a0a` is the same as `0`. + *

+ * Versions schemes cannot change for the comparison. v1.0 /= 1.0-SNAPSHOT /= 1.0. Any changes made to versions schemes must be validated before parsing. * * @param version The version number as an Integer. Example `1234` */ @@ -70,9 +98,7 @@ public int getCalculatedVersion() { } @Override - public int compareTo(@NotNull Object o) { - Version other = (Version) o; - + public int compareTo(@NotNull Version other) { return getCalculatedVersion() - other.getCalculatedVersion(); } From dd67216020dffd9b63cae5e657a96e7d1da830ce Mon Sep 17 00:00:00 2001 From: piitex Date: Mon, 1 Dec 2025 09:09:37 -0600 Subject: [PATCH 24/78] Fixed Windows 11 pathing issues. --- src/main/java/me/piitex/os/OSPathing.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/piitex/os/OSPathing.java b/src/main/java/me/piitex/os/OSPathing.java index 9c4c614..d48d313 100644 --- a/src/main/java/me/piitex/os/OSPathing.java +++ b/src/main/java/me/piitex/os/OSPathing.java @@ -20,7 +20,7 @@ public static File getAppDataDirectory() { String userHome = System.getProperty("user.home"); String os = OSUtil.getOS(); - if (os.equalsIgnoreCase("Windows")) { + if (os.contains("Windows")) { String localAppData = System.getenv("APPDATA"); if (localAppData != null) { return new File(localAppData); From e2cf4b0ccc10a0022014d87daa861189e8dba405 Mon Sep 17 00:00:00 2001 From: piitex Date: Mon, 1 Dec 2025 09:09:47 -0600 Subject: [PATCH 25/78] Removed system print lines. --- src/main/java/me/piitex/os/ZipUtil.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/me/piitex/os/ZipUtil.java b/src/main/java/me/piitex/os/ZipUtil.java index 3ea8035..c6ccbb0 100644 --- a/src/main/java/me/piitex/os/ZipUtil.java +++ b/src/main/java/me/piitex/os/ZipUtil.java @@ -17,7 +17,6 @@ public static void unzipFile(File zip, File outDirectory) throws IOException { } try (ZipInputStream zipIn = new ZipInputStream(new BufferedInputStream(new FileInputStream(zip)))) { ZipEntry entry = zipIn.getNextEntry(); - System.out.println("Entry: " + entry); // iterates over entries in the zip file while (entry != null) { if (!entry.isDirectory()) { @@ -26,7 +25,6 @@ public static void unzipFile(File zip, File outDirectory) throws IOException { } else { // if the entry is a directory, create the directory File dir = new File(outDirectory, entry.getName()); - System.out.println("Making Directory: " + dir); dir.mkdir(); } zipIn.closeEntry(); @@ -36,8 +34,6 @@ public static void unzipFile(File zip, File outDirectory) throws IOException { } private static void extractFile(String name, ZipInputStream zipIn, File output) throws IOException { - System.out.println("Output: " + output.getAbsolutePath()); - System.out.println("File name: " + name); File newFile = new File(output, name); newFile.getParentFile().mkdirs(); try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFile))) { From 7a181b75a46eed35b838b52f7646d0cfd63065fe Mon Sep 17 00:00:00 2001 From: piitex Date: Mon, 1 Dec 2025 09:10:03 -0600 Subject: [PATCH 26/78] Converted system print to logger. --- src/main/java/me/piitex/os/configurations/MasterKeyManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/piitex/os/configurations/MasterKeyManager.java b/src/main/java/me/piitex/os/configurations/MasterKeyManager.java index b595893..1532664 100644 --- a/src/main/java/me/piitex/os/configurations/MasterKeyManager.java +++ b/src/main/java/me/piitex/os/configurations/MasterKeyManager.java @@ -33,7 +33,7 @@ public static char[] getPersistentPassKey() { } private static void generateAndSaveMasterKey() { - System.out.println("Generating new persistent master key..."); + logger.info("Generating a new security key..."); SecureRandom random = new SecureRandom(); byte[] keyBytes = new byte[MASTER_KEY_LENGTH_BYTES]; random.nextBytes(keyBytes); From 5e885fdca1a2fa23d7c5098e55ca30a5d1ea7722 Mon Sep 17 00:00:00 2001 From: piitex Date: Mon, 1 Dec 2025 09:10:28 -0600 Subject: [PATCH 27/78] Added default item to FileChooserOverlay. --- .../me/piitex/engine/overlays/FileChooserOverlay.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/me/piitex/engine/overlays/FileChooserOverlay.java b/src/main/java/me/piitex/engine/overlays/FileChooserOverlay.java index a33d83a..335a376 100644 --- a/src/main/java/me/piitex/engine/overlays/FileChooserOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/FileChooserOverlay.java @@ -17,6 +17,7 @@ public class FileChooserOverlay extends Overlay { private ButtonOverlay button; private FontLoader fontLoader; private String[] fileExtensions; + private String defaultFileName; public FileChooserOverlay(Window window, String text) { this.window = window; @@ -56,6 +57,15 @@ public void onFileSelect(IDirectorySelect iDirectorySelect) { this.directorySelect = iDirectorySelect; } + public String getDefaultFileName() { + return defaultFileName; + } + + public void setDefaultFileName(String defaultFileName) { + this.defaultFileName = defaultFileName; + + } + /** * Set a specific file extension to be use used. You can set both a prefix and a subfix; *.png, filename.*, *.* * @param fileExtensions The array of all valid file extensions. @@ -78,6 +88,7 @@ public Node render() { jfxButton.setOnMouseClicked(event -> { FileChooser chooser = new FileChooser(); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(text, fileExtensions)); + chooser.setInitialFileName(getDefaultFileName()); File directory = chooser.showOpenDialog(window.getStage()); if (getFileSelect() != null) { From fa6501c1185c61c4fe15f70e1f114de169cbf4c4 Mon Sep 17 00:00:00 2001 From: piitex Date: Mon, 1 Dec 2025 09:10:38 -0600 Subject: [PATCH 28/78] Added FileSaveOverlay. --- .../engine/overlays/FileSaveOverlay.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/main/java/me/piitex/engine/overlays/FileSaveOverlay.java diff --git a/src/main/java/me/piitex/engine/overlays/FileSaveOverlay.java b/src/main/java/me/piitex/engine/overlays/FileSaveOverlay.java new file mode 100644 index 0000000..7abbf51 --- /dev/null +++ b/src/main/java/me/piitex/engine/overlays/FileSaveOverlay.java @@ -0,0 +1,103 @@ +package me.piitex.engine.overlays; + +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.stage.FileChooser; +import me.piitex.engine.Window; +import me.piitex.engine.hanlders.events.DirectorySelectEvent; +import me.piitex.engine.loaders.FontLoader; +import me.piitex.engine.overlays.events.IDirectorySelect; + +import java.io.File; + +public class FileSaveOverlay extends Overlay { + private final Window window; + private IDirectorySelect directorySelect; + private String text; + private ButtonOverlay button; + private FontLoader fontLoader; + private String[] fileExtensions; + private String defaultFileName; + + public FileSaveOverlay(Window window, String text) { + this.window = window; + this.text = text; + } + + public FileSaveOverlay(Window window, ButtonOverlay button) { + this.window = window; + this.button = button; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public ButtonOverlay getButton() { + return button; + } + + public FontLoader getFontLoader() { + return fontLoader; + } + + public void setFontLoader(FontLoader fontLoader) { + this.fontLoader = fontLoader; + } + + public IDirectorySelect getFileSelect() { + return directorySelect; + } + + public void onFileSelect(IDirectorySelect iDirectorySelect) { + this.directorySelect = iDirectorySelect; + } + + public String getDefaultFileName() { + return defaultFileName; + } + + public void setDefaultFileName(String defaultFileName) { + this.defaultFileName = defaultFileName; + + } + + /** + * Set a specific file extension to be use used. You can set both a prefix and a subfix; *.png, filename.*, *.* + * @param fileExtensions The array of all valid file extensions. + */ + public void setFileExtensions(String[] fileExtensions) { + this.fileExtensions = fileExtensions; + } + + @Override + public Node render() { + Button jfxButton; + if (button != null) { + jfxButton = (Button) button.render(); + } else { + jfxButton = new Button(text); + jfxButton.setTranslateX(getX()); + jfxButton.setTranslateY(getY()); + } + + jfxButton.setOnMouseClicked(event -> { + FileChooser chooser = new FileChooser(); + chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(text, fileExtensions)); + chooser.setInitialFileName(getDefaultFileName()); + File directory = chooser.showSaveDialog(window.getStage()); + + if (getFileSelect() != null) { + if (directory != null) { + directorySelect.onDirectorySelect(new DirectorySelectEvent(directory)); + } + } + + }); + return jfxButton; + } +} From e341b0cff864d207c4224dd03dfcdad40effa258 Mon Sep 17 00:00:00 2001 From: piitex Date: Mon, 1 Dec 2025 09:11:04 -0600 Subject: [PATCH 29/78] Fixed TextField text being reset after layout changes. --- .../java/me/piitex/engine/overlays/TextFieldOverlay.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/me/piitex/engine/overlays/TextFieldOverlay.java b/src/main/java/me/piitex/engine/overlays/TextFieldOverlay.java index 152848b..9411779 100644 --- a/src/main/java/me/piitex/engine/overlays/TextFieldOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/TextFieldOverlay.java @@ -36,6 +36,8 @@ public TextFieldOverlay(String defaultInput, String hintText, double x, double y this.width = width; this.height = height; setNode(textField); + textField.setText(defaultInput); + textField.setPromptText(hintText); setX(x); setY(y); } @@ -86,11 +88,7 @@ public Node render() { if (getMaxWidth() > 0 || getMaxHeight() > 0) { textField.setMaxSize(getMaxWidth(), getMaxHeight()); } - textField.setPromptText(hintText); - textField.setText(defaultInput); textField.setAlignment(Pos.TOP_LEFT); - - textField.textProperty().addListener((observable, oldValue, newValue) -> { if (getiInputSetEvent() != null) { iInputSetEvent.onInputSet(new InputSetEvent(this, newValue)); From 94fba5c6d618eb8a136b6edcf6d01ff0c7b60360 Mon Sep 17 00:00:00 2001 From: piitex Date: Mon, 1 Dec 2025 09:11:33 -0600 Subject: [PATCH 30/78] Replaced system err with logger. --- .../java/me/piitex/engine/overlays/TextFlowOverlay.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java b/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java index 88be327..37c3c4e 100644 --- a/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java @@ -8,6 +8,8 @@ import javafx.scene.text.Text; import javafx.scene.text.TextFlow; import me.piitex.engine.loaders.FontLoader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.LinkedList; @@ -19,7 +21,7 @@ public class TextFlowOverlay extends Overlay implements Region { private FontLoader font; private FontSmoothingType fontSmoothingType = FontSmoothingType.GRAY; private double width, height, prefWidth, prefHeight, maxWidth, maxHeight; - private double scaleWidth, scaleHeight; + private static final Logger logger = LoggerFactory.getLogger(TextFlowOverlay.class); public TextFlowOverlay(String text, double width, double height) { this.textFlow = new TextFlow(); @@ -167,7 +169,7 @@ public Node render() { inputField.setFont(font); } } - default -> System.out.println("Unsupported overlay in TextFlow. {}" + overlay.toString()); + default -> logger.error("Unsupported overlay in TextFlow. {}", overlay.toString()); } Node node = overlay.assemble(); From 9379c4586e62acb3adb8757bc376bc12e68cb996 Mon Sep 17 00:00:00 2001 From: piitex Date: Mon, 1 Dec 2025 09:12:21 -0600 Subject: [PATCH 31/78] Fixed rendering order issues when the index was being shuffled. --- src/main/java/me/piitex/engine/Renderer.java | 27 +++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/java/me/piitex/engine/Renderer.java b/src/main/java/me/piitex/engine/Renderer.java index 2c32b88..71a536b 100644 --- a/src/main/java/me/piitex/engine/Renderer.java +++ b/src/main/java/me/piitex/engine/Renderer.java @@ -10,6 +10,8 @@ import me.piitex.engine.hanlders.events.LayoutRenderEvent; import me.piitex.engine.layouts.Layout; import me.piitex.engine.overlays.Overlay; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.*; @@ -30,9 +32,10 @@ public class Renderer extends Element { private double borderWidth = 1; private final List styles = new ArrayList<>(); private Window window; - private final Properties properties = new Properties(); + private static final Logger logger = LoggerFactory.getLogger(Renderer.class); + // Handlers for events private IRendererKey iRendererKey; @@ -192,9 +195,10 @@ public void addElement(Element element) { */ public void addElement(Element element, int index) { Element current = elements.get(index); - if (current != null) { + if (current != null && current != element) { int i = index + 1; addElement(getElementAt(index), i); + elements.remove(index); } element.setIndex(index); elements.put(index, element); @@ -279,12 +283,29 @@ public void replaceElement(int index, Element element) { public void addToView(Node node, int index) { if (getNode() instanceof Pane pane) { if (!pane.getChildren().contains(node)) { - if (index < pane.getChildren().size() && index != 0) { + if (index >= 0 && index <= pane.getChildren().size()) { + pane.getChildren().add(index, node); + } else { + // Fallback for an invalid index + pane.getChildren().add(node); + } + } else { + pane.getChildren().remove(node); + + // Check index again after removing it. + if (index <= pane.getChildren().size()) { + logger.warn("Replacing '{}' with '{}'", index, node.getClass().toString()); pane.getChildren().add(index, node); } else { + // If the new index is out of bounds, add it to the end. + logger.debug("Could not allocate space. Node will be rendered first."); pane.getChildren().add(node); } + pane.requestLayout(); + logger.debug("Node already exists in renderer. Shuffling '{}' forward.", node.getClass().toString()); } + } else { + logger.error("Invalid renderer type!", new RuntimeException()); } } From 5507d2112a9e9f9136c2e1535c704f1430810900 Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 6 Dec 2025 20:01:49 -0600 Subject: [PATCH 32/78] Added commons-compress and plexus-archiver. --- pom.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pom.xml b/pom.xml index 6ea08a6..92bb747 100644 --- a/pom.xml +++ b/pom.xml @@ -119,6 +119,17 @@ 6.7 + + org.apache.commons + commons-compress + 1.28.0 + + + org.codehaus.plexus + plexus-archiver + 4.10.4 + + From 8546c1ca1e548123a4f884fd9469150b213c932a Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 6 Dec 2025 20:02:39 -0600 Subject: [PATCH 33/78] Added key parameter to set static pass keys. --- .../piitex/os/configurations/FileCrypter.java | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/main/java/me/piitex/os/configurations/FileCrypter.java b/src/main/java/me/piitex/os/configurations/FileCrypter.java index da2c3ea..6e56da7 100644 --- a/src/main/java/me/piitex/os/configurations/FileCrypter.java +++ b/src/main/java/me/piitex/os/configurations/FileCrypter.java @@ -1,6 +1,10 @@ package me.piitex.os.configurations; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import javax.crypto.*; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.PBEKeySpec; @@ -33,10 +37,13 @@ public class FileCrypter { private static final int GCM_IV_LENGTH_BYTES = 12; private static final int GCM_TAG_LENGTH_BITS = 128; - // RECOMMENDED TO CHANGE - private static final String passKey = System.getProperty("user.name") + System.getProperty("user.home") + System.getProperty("os.name"); + private static final Logger logger = LoggerFactory.getLogger(FileCrypter.class); + + public static void encryptFile(File newFile, File outputFile) { + encryptFile(newFile, outputFile, null); + } - public static void encryptFile(File file, File outputFile) { + public static void encryptFile(File newFile, File outputFile, @Nullable String key) { SecureRandom secureRandom = new SecureRandom(); byte[] salt = new byte[SALT_LENGTH_BYTES]; secureRandom.nextBytes(salt); // Generate a unique salt for each encryption @@ -44,9 +51,14 @@ public static void encryptFile(File file, File outputFile) { byte[] iv = new byte[GCM_IV_LENGTH_BYTES]; // Generate a unique IV for each encryption secureRandom.nextBytes(iv); - char[] passKeyChars = MasterKeyManager.getPersistentPassKey(); + char[] passKeyChars; + if (key == null) { + passKeyChars = MasterKeyManager.getPersistentPassKey(); + } else { + passKeyChars = key.toCharArray(); + } - try (FileInputStream inputStream = new FileInputStream(file); + try (FileInputStream inputStream = new FileInputStream(newFile); FileOutputStream outputStream = new FileOutputStream(outputFile)) { SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF_ALG); @@ -78,12 +90,23 @@ public static void encryptFile(File file, File outputFile) { } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException | IOException e) { - throw new RuntimeException("Error encrypting file: " + e.getMessage(), e); + logger.error("Error occurred while encrypting '{}'.", newFile.getAbsolutePath(), e); } } public static void decryptFile(File file, File outputFile) throws IllegalBlockSizeException, IOException { - char[] passKeyChars = MasterKeyManager.getPersistentPassKey(); + decryptFile(file, outputFile, null); + } + + public static void decryptFile(File file, File outputFile, @Nullable String key) throws IllegalBlockSizeException, IOException { + + char[] passKeyChars; + if (key == null) { + passKeyChars = MasterKeyManager.getPersistentPassKey(); + } else { + passKeyChars = key.toCharArray(); + } + try (FileInputStream inputStream = new FileInputStream(file); FileOutputStream outputStream = new FileOutputStream(outputFile)) { @@ -124,7 +147,7 @@ public static void decryptFile(File file, File outputFile) throws IllegalBlockSi outputStream.write(outputBytes); } } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | BadPaddingException e) { - throw new RuntimeException("Error decrypting file: " + e.getMessage(), e); + logger.error("Error occurred while decrypting '{}'.", file.getAbsolutePath(), e); } } } \ No newline at end of file From 35cf2d9b45529d6d5b6165e8978af7d4482fcf10 Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 6 Dec 2025 20:03:06 -0600 Subject: [PATCH 34/78] Exception will now be logged instead of thrown. --- src/main/java/me/piitex/os/configurations/InfoFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/piitex/os/configurations/InfoFile.java b/src/main/java/me/piitex/os/configurations/InfoFile.java index f1d81d0..c1473c5 100644 --- a/src/main/java/me/piitex/os/configurations/InfoFile.java +++ b/src/main/java/me/piitex/os/configurations/InfoFile.java @@ -48,7 +48,7 @@ public InfoFile(File file, boolean encrypt) { logger.warn("Unable to create file '{}'", file.getAbsolutePath()); } } catch (IOException e) { - throw new RuntimeException(e); + logger.error("IO exception occurred while creating character info!", e); } } else { // Load file From b27755e33e2ab6e6bd3b778b43c6bca31d83ea97 Mon Sep 17 00:00:00 2001 From: piitex Date: Sat, 6 Dec 2025 20:04:06 -0600 Subject: [PATCH 35/78] Added logging for api calls. --- src/main/java/me/piitex/os/GitHubUtil.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/piitex/os/GitHubUtil.java b/src/main/java/me/piitex/os/GitHubUtil.java index 15f4f5d..24c839f 100644 --- a/src/main/java/me/piitex/os/GitHubUtil.java +++ b/src/main/java/me/piitex/os/GitHubUtil.java @@ -2,6 +2,8 @@ import org.json.JSONArray; import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.*; import java.net.HttpURLConnection; @@ -14,7 +16,7 @@ */ public class GitHubUtil { private final String repositoryUrl; //https://api.github.com/repos/owner/repo/ - + private static final Logger logger = LoggerFactory.getLogger(GitHubUtil.class); /** * Initializes the base url for the repository. To get the repository link, use the following format. *

@@ -33,6 +35,7 @@ public GitHubUtil(String repositoryUrl) {
      */
     public JSONObject getLatestReleaseJson() throws IOException, URISyntaxException {
         URL url = new URI(repositoryUrl + "releases/latest").toURL();
+        logger.info("Fetching latest release request '{}'", url.toString());
         HttpURLConnection connection = (HttpURLConnection) url.openConnection();
         connection.setConnectTimeout(1000);
         connection.setRequestMethod("GET");
@@ -59,6 +62,7 @@ public int getLatestReleaseID() throws IOException, URISyntaxException {
 
     public JSONArray getReleaseAssets(int releaseID) throws IOException, URISyntaxException {
         URL url = new URI(repositoryUrl + "releases/" + releaseID + "/assets").toURL();
+        logger.info("Fetching release asset '{}' '{}'", releaseID, url.toString());
         HttpURLConnection connection = (HttpURLConnection) url.openConnection();
 
         connection.setRequestMethod("GET");

From 974c504867f22a5c4fee5fc828728da3e04fa293 Mon Sep 17 00:00:00 2001
From: piitex 
Date: Sat, 6 Dec 2025 20:04:46 -0600
Subject: [PATCH 36/78] Added tar.gz file support.

---
 src/main/java/me/piitex/os/ZipUtil.java | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/main/java/me/piitex/os/ZipUtil.java b/src/main/java/me/piitex/os/ZipUtil.java
index c6ccbb0..fe5e8e5 100644
--- a/src/main/java/me/piitex/os/ZipUtil.java
+++ b/src/main/java/me/piitex/os/ZipUtil.java
@@ -1,5 +1,6 @@
 package me.piitex.os;
 
+import org.codehaus.plexus.archiver.tar.TarGZipUnArchiver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,6 +46,17 @@ private static void extractFile(String name, ZipInputStream zipIn, File output)
         }
     }
 
+    public static void unzipTarGzFile(File tarGz, File outDirectory) {
+        if (!outDirectory.exists()) {
+            outDirectory.mkdir();
+        }
+
+        TarGZipUnArchiver unArchiver = new TarGZipUnArchiver();
+        unArchiver.setSourceFile(tarGz);
+        unArchiver.setDestDirectory(outDirectory);
+        unArchiver.extract();
+    }
+
     public static void zipDirectory(File zipFile, File directory) {
         if (!directory.isDirectory()) {
             return;

From 303161879c15c4f04543e46f596913c5a11ac84a Mon Sep 17 00:00:00 2001
From: piitex 
Date: Sun, 7 Dec 2025 08:43:37 -0600
Subject: [PATCH 37/78] Button icon will now use IconOverlay.

---
 src/main/java/me/piitex/engine/overlays/ButtonBuilder.java | 7 +++----
 src/main/java/me/piitex/engine/overlays/ButtonOverlay.java | 5 +++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/main/java/me/piitex/engine/overlays/ButtonBuilder.java b/src/main/java/me/piitex/engine/overlays/ButtonBuilder.java
index 9dc3578..a21a8e0 100644
--- a/src/main/java/me/piitex/engine/overlays/ButtonBuilder.java
+++ b/src/main/java/me/piitex/engine/overlays/ButtonBuilder.java
@@ -3,7 +3,6 @@
 import javafx.scene.paint.Paint;
 import me.piitex.engine.Element;
 import me.piitex.engine.loaders.FontLoader;
-import org.kordamp.ikonli.javafx.FontIcon;
 
 import java.util.ArrayList;
 import java.util.LinkedList;
@@ -12,7 +11,7 @@
 public class ButtonBuilder {
     private final String id;
     private String text;
-    private FontIcon icon;
+    private IconOverlay icon;
     private FontLoader font;
     private Element graphic;
     private Paint textFill;
@@ -45,7 +44,7 @@ public ButtonBuilder setText(String text) {
      * @param icon The FontIcon to display on the button.
      * @return The builder instance.
      */
-    public ButtonBuilder setIcon(FontIcon icon) {
+    public ButtonBuilder setIcon(IconOverlay icon) {
         this.icon = icon;
         return this;
     }
@@ -188,7 +187,7 @@ public String getText() {
         return text;
     }
 
-    public FontIcon getIcon() {
+    public IconOverlay getIcon() {
         return icon;
     }
 
diff --git a/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java b/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java
index 6a65a05..483a64d 100644
--- a/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java
+++ b/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java
@@ -18,7 +18,7 @@ public class ButtonOverlay extends Overlay implements Region {
     private String text;
     private FontLoader font;
     private Element graphic;
-    private final FontIcon icon;
+    private final IconOverlay icon;
     private final LinkedList images = new LinkedList<>();
     private Paint textFill;
     private Pos alignment;
@@ -117,7 +117,8 @@ public Node render() {
         }
 
         if (icon != null) {
-            button.setGraphic(icon);
+            Node graphic = icon.assemble();
+            button.setGraphic(graphic);
         }
         if (text != null && !text.isEmpty()) {
             button.setText(text);

From d5a3f686efca39a639b022b20ecad4938d381d53 Mon Sep 17 00:00:00 2001
From: piitex 
Date: Sun, 7 Dec 2025 08:43:52 -0600
Subject: [PATCH 38/78] Icon color now defaults to white.

---
 src/main/java/me/piitex/engine/overlays/IconOverlay.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/java/me/piitex/engine/overlays/IconOverlay.java b/src/main/java/me/piitex/engine/overlays/IconOverlay.java
index 93d0e3f..5cbb394 100644
--- a/src/main/java/me/piitex/engine/overlays/IconOverlay.java
+++ b/src/main/java/me/piitex/engine/overlays/IconOverlay.java
@@ -1,6 +1,7 @@
 package me.piitex.engine.overlays;
 
 import javafx.scene.Node;
+import javafx.scene.paint.Color;
 import javafx.scene.paint.Paint;
 import org.kordamp.ikonli.Ikon;
 import org.kordamp.ikonli.javafx.FontIcon;
@@ -12,7 +13,7 @@ public class IconOverlay extends Overlay {
     private final FontIcon fontIcon;
     private String iconCode;
     private int iconSize = 16;
-    private Paint color;
+    private Paint color = Color.WHITE;
 
     public IconOverlay() {
         this.fontIcon = new FontIcon();

From ccc4ac753f2dc9dd1d4805b860f262f65e7466cc Mon Sep 17 00:00:00 2001
From: piitex 
Date: Thu, 11 Dec 2025 05:43:59 -0600
Subject: [PATCH 39/78] Removed FontIcon parameter.

---
 .../piitex/engine/overlays/TextOverlay.java   | 103 +++++++-----------
 1 file changed, 37 insertions(+), 66 deletions(-)

diff --git a/src/main/java/me/piitex/engine/overlays/TextOverlay.java b/src/main/java/me/piitex/engine/overlays/TextOverlay.java
index d07fbff..bb9f064 100644
--- a/src/main/java/me/piitex/engine/overlays/TextOverlay.java
+++ b/src/main/java/me/piitex/engine/overlays/TextOverlay.java
@@ -5,55 +5,48 @@
 import javafx.scene.text.FontSmoothingType;
 import javafx.scene.text.Text;
 import me.piitex.engine.loaders.FontLoader;
-import org.kordamp.ikonli.javafx.FontIcon;
 
 public class TextOverlay extends Overlay {
-    private final Node node;
     private String string;
     private Color textFillColor;
     private FontLoader fontLoader;
     private FontSmoothingType fontSmoothingType;
     private boolean strikeout, underline;
-
+    private final Text textNode;
 
     public TextOverlay(String text) {
         this.string = text;
-        this.node = new Text();
-        setNode(node);
-    }
-
-    public TextOverlay(FontIcon icon) {
-        this.node = icon;
-        setNode(node);
+        this.textNode = new Text();
+        setNode(textNode);
     }
 
     public TextOverlay(String text, FontLoader fontLoader) {
         this.string = text;
         this.fontLoader = fontLoader;
-        this.node = new Text();
-        setNode(node);
+        this.textNode = new Text();
+        setNode(textNode);
     }
 
     public TextOverlay(String text, Color textFillColor) {
         this.string = text;
         this.textFillColor = textFillColor;
-        this.node = new Text();
-        setNode(node);
+        this.textNode = new Text();
+        setNode(textNode);
     }
 
     public TextOverlay(String text, Color textFillColor, FontLoader fontLoader) {
         this.string = text;
         this.textFillColor = textFillColor;
         this.fontLoader = fontLoader;
-        this.node = new Text();
-        setNode(node);
+        this.textNode = new Text();
+        setNode(textNode);
     }
 
     public TextOverlay(String text, FontLoader fontLoader, double x, double y) {
         this.string = text;
         this.fontLoader = fontLoader;
-        this.node = new Text();
-        setNode(node);
+        this.textNode = new Text();
+        setNode(textNode);
         setX(x);
         setY(y);
     }
@@ -62,8 +55,8 @@ public TextOverlay(String text, Color textFillColor, FontLoader fontLoader, int
         this.string = text;
         this.textFillColor = textFillColor;
         this.fontLoader = fontLoader;
-        this.node = new Text();
-        setNode(node);
+        this.textNode = new Text();
+        setNode(textNode);
         setX(x);
         setY(y);
     }
@@ -78,47 +71,37 @@ public void setFontSmoothingType(FontSmoothingType fontSmoothingType) {
 
     @Override
     public Node render() {
-        if (node instanceof FontIcon icon) {
-            if (textFillColor != null) {
-                icon.setIconColor(textFillColor);
-            }
-        } else if (node instanceof Text text) {
-            // If text is set, render a Text node.
-            text.setText(string);
-            text.setFontSmoothingType(fontSmoothingType);
-            if (textFillColor != null) {
-                text.setFill(textFillColor);
-            }
-
-            if (fontLoader != null) {
-                text.setFont(fontLoader.getFont());
-            }
-            if (getTextFillColor() != null) {
-                text.setFill(getTextFillColor());
-            }
-            text.setStrikethrough(strikeout);
-            text.setUnderline(underline);
+        // If text is set, render a Text node.
+        textNode.setText(string);
+        textNode.setFontSmoothingType(fontSmoothingType);
+        if (textFillColor != null) {
+            textNode.setFill(textFillColor);
         }
 
-        if (node != null) {
-            node.setTranslateX(getX());
-            node.setTranslateY(getY());
-            node.getStyleClass().addAll(getStyles());
+        if (fontLoader != null) {
+            textNode.setFont(fontLoader.getFont());
+        }
+        if (getTextFillColor() != null) {
+            textNode.setFill(getTextFillColor());
         }
+        textNode.setStrikethrough(strikeout);
+        textNode.setUnderline(underline);
 
-        return node;
+
+        textNode.setTranslateX(getX());
+        textNode.setTranslateY(getY());
+        textNode.getStyleClass().addAll(getStyles());
+
+        return textNode;
     }
 
-    public String getText() {
+    public String getTextNode() {
         return string;
     }
 
     public void setText(String text) {
         this.string = text;
-
-        if (node instanceof Text t) {
-            t.setText(text);
-        }
+        textNode.setText(text);
     }
 
     public boolean isStrikeout() {
@@ -127,10 +110,7 @@ public boolean isStrikeout() {
 
     public void setStrikeout(boolean strikeout) {
         this.strikeout = strikeout;
-
-        if (node instanceof Text t) {
-            t.setStrikethrough(strikeout);
-        }
+        textNode.setStrikethrough(strikeout);
     }
 
     public boolean isUnderline() {
@@ -139,10 +119,7 @@ public boolean isUnderline() {
 
     public void setUnderline(boolean underline) {
         this.underline = underline;
-
-        if (node instanceof Text t) {
-            t.setUnderline(strikeout);
-        }
+        textNode.setUnderline(strikeout);
     }
 
     public FontLoader getFontLoader() {
@@ -151,10 +128,7 @@ public FontLoader getFontLoader() {
 
     public void setFont(FontLoader fontLoader) {
         this.fontLoader = fontLoader;
-
-        if (node instanceof Text t) {
-            t.setFont(fontLoader.getFont());
-        }
+        textNode.setFont(fontLoader.getFont());
     }
 
     public Color getTextFillColor() {
@@ -163,10 +137,7 @@ public Color getTextFillColor() {
 
     public void setTextFill(Color textFillColor) {
         this.textFillColor = textFillColor;
-
-        if (node instanceof Text t) {
-            t.setFill(textFillColor);
-        }
+        textNode.setFill(textFillColor);
     }
 
 }

From 725bf3c30ce6d91f725c997f1f45dfe9ac349c37 Mon Sep 17 00:00:00 2001
From: piitex 
Date: Thu, 11 Dec 2025 05:44:23 -0600
Subject: [PATCH 40/78] Added text alignment.

---
 .../me/piitex/engine/overlays/TextFlowOverlay.java    |  2 +-
 .../java/me/piitex/engine/overlays/TextOverlay.java   | 11 +++++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java b/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java
index 37c3c4e..b97637e 100644
--- a/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java
+++ b/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java
@@ -139,7 +139,7 @@ public Node render() {
             // Check node type
             switch (overlay) {
                 case TextOverlay text1 -> {
-                    text1.setText(text1.getText().replace("\\n", System.lineSeparator()));
+                    text1.setText(text1.getTextNode().replace("\\n", System.lineSeparator()));
                     if (font != null && text1.getFontLoader() == null) {
                         // Passes
                         text1.setFont(font);
diff --git a/src/main/java/me/piitex/engine/overlays/TextOverlay.java b/src/main/java/me/piitex/engine/overlays/TextOverlay.java
index bb9f064..5138b48 100644
--- a/src/main/java/me/piitex/engine/overlays/TextOverlay.java
+++ b/src/main/java/me/piitex/engine/overlays/TextOverlay.java
@@ -4,13 +4,16 @@
 import javafx.scene.paint.Color;
 import javafx.scene.text.FontSmoothingType;
 import javafx.scene.text.Text;
+import javafx.scene.text.TextAlignment;
 import me.piitex.engine.loaders.FontLoader;
+import org.kordamp.ikonli.javafx.FontIcon;
 
 public class TextOverlay extends Overlay {
     private String string;
     private Color textFillColor;
     private FontLoader fontLoader;
     private FontSmoothingType fontSmoothingType;
+    private TextAlignment textAlignment;
     private boolean strikeout, underline;
     private final Text textNode;
 
@@ -84,6 +87,9 @@ public Node render() {
         if (getTextFillColor() != null) {
             textNode.setFill(getTextFillColor());
         }
+        if (textAlignment != null) {
+            textNode.setTextAlignment(textAlignment);
+        }
         textNode.setStrikethrough(strikeout);
         textNode.setUnderline(underline);
 
@@ -140,4 +146,9 @@ public void setTextFill(Color textFillColor) {
         textNode.setFill(textFillColor);
     }
 
+    public void setTextAlignment(TextAlignment textAlignment) {
+        this.textAlignment = textAlignment;
+        textNode.setTextAlignment(textAlignment);
+    }
+
 }

From d6a8f237a099a636a23f725c2397f2497f537d12 Mon Sep 17 00:00:00 2001
From: piitex 
Date: Thu, 11 Dec 2025 05:44:45 -0600
Subject: [PATCH 41/78] Changed linux appdata back to .local.

---
 src/main/java/me/piitex/os/OSPathing.java | 22 +---------------------
 1 file changed, 1 insertion(+), 21 deletions(-)

diff --git a/src/main/java/me/piitex/os/OSPathing.java b/src/main/java/me/piitex/os/OSPathing.java
index d48d313..dcd7d51 100644
--- a/src/main/java/me/piitex/os/OSPathing.java
+++ b/src/main/java/me/piitex/os/OSPathing.java
@@ -9,8 +9,6 @@
 public class OSPathing {
     private static final Logger logger = LoggerFactory.getLogger(OSPathing.class);
 
-    public static String groupId = "me.piitex.app.App"; // Only used for linux
-
     public static File getDocumentsDirectory() {
         String userHome = System.getProperty("user.home");
         return new File(userHome + File.separator + "Documents");
@@ -26,18 +24,7 @@ public static File getAppDataDirectory() {
                 return new File(localAppData);
             }
         } else if (os.contains("Linux")) {
-            validateAppData();
-            File file = new File(userHome + "/.var/app/" + groupId + "/");
-            if (!file.exists()) {
-                try {
-                    if (file.createNewFile()) {
-                        logger.info("Created appdata directory...");
-                    }
-                } catch (IOException e) {
-                    logger.error("Could not create appdata folder!", e);
-                }
-            }
-            return new File(userHome + "/.var/app/" + groupId + "/");
+            return new File(userHome + "/.local/share/");
         }
 
         return new File(userHome + "/");
@@ -74,11 +61,4 @@ public static void initiateProjectDirectories(String project, boolean documents,
             }
         }
     }
-
-    private static void validateAppData() {
-        if (OSUtil.getOS().contains("Linux") && groupId.equals("me.piitex.app.App")) {
-            throw new RuntimeException("You must set the linux group id for the application! OSPathing.groupId = \"your.group.app\"");
-        }
-    }
-
 }

From 572f2b4456b96cc3b450dbeee3122b0d264fad90 Mon Sep 17 00:00:00 2001
From: piitex 
Date: Fri, 12 Dec 2025 08:07:50 -0600
Subject: [PATCH 42/78] Added RingProgressOverlay.

---
 .../engine/overlays/RingProgressOverlay.java  | 117 ++++++++++++++++++
 1 file changed, 117 insertions(+)
 create mode 100644 src/main/java/me/piitex/engine/overlays/RingProgressOverlay.java

diff --git a/src/main/java/me/piitex/engine/overlays/RingProgressOverlay.java b/src/main/java/me/piitex/engine/overlays/RingProgressOverlay.java
new file mode 100644
index 0000000..28bc1a8
--- /dev/null
+++ b/src/main/java/me/piitex/engine/overlays/RingProgressOverlay.java
@@ -0,0 +1,117 @@
+package me.piitex.engine.overlays;
+
+import atlantafx.base.controls.RingProgressIndicator;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.Node;
+
+public class RingProgressOverlay extends Overlay implements Region {
+    private double width, height, prefWidth, prefHeight, maxWidth, maxHeight;
+    private double scaleWidth, scaleHeight;
+    private final RingProgressIndicator progressBar;
+
+    public RingProgressOverlay() {
+        this.progressBar = new RingProgressIndicator();
+        setNode(progressBar);
+    }
+
+    public void bind(ObservableValue binding) {
+        progressBar.progressProperty().bind(binding);
+    }
+
+    @Override
+    public Node render() {
+        progressBar.setTranslateX(getX());
+        progressBar.setTranslateY(getY());
+
+        if (getWidth() > 0 || getHeight() > 0) {
+            progressBar.setMinSize(width, height);
+        }
+        if (getPrefWidth() > 0 || getPrefHeight() > 0) {
+            progressBar.setPrefSize(getPrefWidth(), getPrefHeight());
+        }
+        if (getMaxWidth() > 0 || getMaxHeight() > 0) {
+            progressBar.setMaxSize(getMaxWidth(), getMaxHeight());
+        }
+
+        progressBar.getStyleClass().addAll(getStyles());
+
+        return progressBar;
+    }
+
+    public RingProgressIndicator getProgressBar() {
+        return progressBar;
+    }
+
+    @Override
+    public double getWidth() {
+        return width;
+    }
+
+    @Override
+    public double getHeight() {
+        return height;
+    }
+
+    @Override
+    public void setWidth(double w) {
+        this.width = w;
+        progressBar.setMinWidth(w);
+    }
+
+    @Override
+    public void setHeight(double h) {
+        this.height = h;
+        progressBar.setMinHeight(h);
+    }
+
+    @Override
+    public double getPrefWidth() {
+        return prefWidth;
+    }
+
+    @Override
+    public double getPrefHeight() {
+        return prefHeight;
+    }
+
+    @Override
+    public void setPrefWidth(double w) {
+        this.prefWidth = w;
+        progressBar.setPrefWidth(w);
+    }
+
+    @Override
+    public void setPrefHeight(double h) {
+        this.prefHeight = h;
+        progressBar.setPrefHeight(h);
+    }
+
+    @Override
+    public double getMaxWidth() {
+        return maxWidth;
+    }
+
+    @Override
+    public double getMaxHeight() {
+        return maxHeight;
+    }
+
+    @Override
+    public void setMaxWidth(double w) {
+        this.maxWidth = w;
+        progressBar.setMaxWidth(w);
+    }
+
+    @Override
+    public void setMaxHeight(double h) {
+        this.maxHeight = h;
+        progressBar.setMaxHeight(h);
+    }
+
+    @Override
+    public void setMaxSize(double w, double h) {
+        this.maxWidth = w;
+        this.maxHeight = h;
+        progressBar.setMaxSize(w, h);
+    }
+}

From 6a5918d9df6bca0a0b75d2ed50ec5a7ef5fb336e Mon Sep 17 00:00:00 2001
From: piitex 
Date: Fri, 12 Dec 2025 08:08:30 -0600
Subject: [PATCH 43/78] Fixed height issues with the window.

---
 src/main/java/me/piitex/engine/Window.java | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/main/java/me/piitex/engine/Window.java b/src/main/java/me/piitex/engine/Window.java
index 4c2e2e1..7bf0ded 100644
--- a/src/main/java/me/piitex/engine/Window.java
+++ b/src/main/java/me/piitex/engine/Window.java
@@ -21,6 +21,7 @@
 import me.piitex.engine.layouts.Layout;
 import me.piitex.engine.loaders.ImageLoader;
 import me.piitex.engine.overlays.*;
+import me.piitex.os.OSUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -153,7 +154,18 @@ protected void buildStage() {
         stage.setTitle(title);
         stage.initStyle(stageStyle);
         stage.setWidth(width);
-        stage.setHeight(height);
+
+        // Linux, Windows, and Mac handle sizing of windows differently.
+        // With the top control bar enabled, the height will be off.
+        // Offset the height by subtracting the height of the top bar.
+        if (OSUtil.getOS().contains("Windows")) {
+            stage.setHeight(height + 40);
+        } else if (OSUtil.getOS().contains("Linux")) {
+            stage.setHeight(height);
+        } else {
+            stage.setHeight(height);
+        }
+
         stage.setMaximized(maximized);
         stage.setFullScreen(fullscreen);
 
@@ -297,7 +309,6 @@ public void addContainer(Container container, Node node) {
         addContainer(container, node, container.getIndex());
     }
 
-
     /**
      * 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.

From 43fa781da40992f9690f2056ec416370e0da13f4 Mon Sep 17 00:00:00 2001
From: piitex 
Date: Fri, 12 Dec 2025 08:08:54 -0600
Subject: [PATCH 44/78] Node will reflect scroll position changes.

---
 src/main/java/me/piitex/engine/containers/ScrollContainer.java | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/java/me/piitex/engine/containers/ScrollContainer.java b/src/main/java/me/piitex/engine/containers/ScrollContainer.java
index 25740b5..7bfa7ee 100644
--- a/src/main/java/me/piitex/engine/containers/ScrollContainer.java
+++ b/src/main/java/me/piitex/engine/containers/ScrollContainer.java
@@ -81,6 +81,7 @@ public void setScrollWhenNeeded(boolean scrollWhenNeeded) {
 
     public void setScrollPosition(double scrollPosition) {
         this.scrollPosition = scrollPosition;
+        scrollPane.setVvalue(scrollPosition);
     }
 
     public void setScrollToBottom(boolean scrollToBottom) {

From 917602db53381b33cc06b5df8f2e3604ee0dd294 Mon Sep 17 00:00:00 2001
From: HackusatePvP 
Date: Wed, 17 Dec 2025 15:30:19 -0600
Subject: [PATCH 45/78] Removed invalid border position.

---
 .../engine/containers/BorderContainer.java    | 34 ++++---------------
 1 file changed, 6 insertions(+), 28 deletions(-)

diff --git a/src/main/java/me/piitex/engine/containers/BorderContainer.java b/src/main/java/me/piitex/engine/containers/BorderContainer.java
index ea5491a..7aa1f0a 100644
--- a/src/main/java/me/piitex/engine/containers/BorderContainer.java
+++ b/src/main/java/me/piitex/engine/containers/BorderContainer.java
@@ -8,7 +8,7 @@
 public class BorderContainer extends Container {
     private final BorderPane pane;
 
-    private Element top, bottom, right, left, center, border;
+    private Element top, bottom, right, left, center;
 
     public BorderContainer(double x, double y, double width, double height, int index) {
         BorderPane tempPane = new BorderPane();
@@ -38,6 +38,7 @@ public Element getTop() {
 
     public void setTop(Element top) {
         this.top = top;
+        pane.setTop(top.assemble());
     }
 
     public Element getBottom() {
@@ -46,6 +47,7 @@ public Element getBottom() {
 
     public void setBottom(Element bottom) {
         this.bottom = bottom;
+        pane.setBottom(bottom.assemble());
     }
 
     public Element getRight() {
@@ -54,6 +56,7 @@ public Element getRight() {
 
     public void setRight(Element right) {
         this.right = right;
+        pane.setRight(right.assemble());
     }
 
     public Element getLeft() {
@@ -62,6 +65,7 @@ public Element getLeft() {
 
     public void setLeft(Element left) {
         this.left = left;
+        pane.setLeft(left.assemble());
     }
 
     public Element getCenter() {
@@ -70,14 +74,7 @@ public Element getCenter() {
 
     public void setCenter(Element center) {
         this.center = center;
-    }
-
-    public Element getBorder() {
-        return border;
-    }
-
-    public void setBorder(Element border) {
-        this.border = border;
+        pane.setCenter(center.assemble());
     }
 
     @Override
@@ -105,25 +102,6 @@ public Node build() {
             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);
 
         if (getOnClick() != null) {

From c20b8e999a6b2c64df2effb09fdff2692300837e Mon Sep 17 00:00:00 2001
From: HackusatePvP 
Date: Wed, 17 Dec 2025 15:30:38 -0600
Subject: [PATCH 46/78] Removed old RenJava image pathing.

---
 .../java/me/piitex/engine/loaders/ImageLoader.java | 14 ++------------
 .../me/piitex/engine/overlays/ImageOverlay.java    | 11 -----------
 2 files changed, 2 insertions(+), 23 deletions(-)

diff --git a/src/main/java/me/piitex/engine/loaders/ImageLoader.java b/src/main/java/me/piitex/engine/loaders/ImageLoader.java
index 080ab24..67339e8 100644
--- a/src/main/java/me/piitex/engine/loaders/ImageLoader.java
+++ b/src/main/java/me/piitex/engine/loaders/ImageLoader.java
@@ -26,18 +26,8 @@ public class ImageLoader {
 
     public static boolean useCache = true;
 
-    /**
-     * Loads an image via a filename from the base directory.
-     * @param name Name of the image file.
-     */
-    public ImageLoader(String name) {
-        File directory = new File(System.getProperty("user.dir") + "/game/images/");
-        this.file = new File(directory, name);
-    }
-
-    public ImageLoader(String directory, String name) {
-        File fileDirectory = new File(System.getProperty("user.dir") + "/" + directory + "/");
-        this.file = new File(fileDirectory, name);
+    public ImageLoader(String path) {
+        this.file = new File(path);
     }
 
     public ImageLoader(File file) {
diff --git a/src/main/java/me/piitex/engine/overlays/ImageOverlay.java b/src/main/java/me/piitex/engine/overlays/ImageOverlay.java
index 3448349..0133b41 100644
--- a/src/main/java/me/piitex/engine/overlays/ImageOverlay.java
+++ b/src/main/java/me/piitex/engine/overlays/ImageOverlay.java
@@ -53,17 +53,6 @@ public ImageOverlay(String imagePath) {
         setNode(imageView);
     }
 
-    public ImageOverlay(String directory, String imagePath) {
-        ImageLoader loader = new ImageLoader(directory, imagePath);
-        this.image = loader.build();
-        this.path = loader.getFile().getAbsolutePath();
-        this.fileName = loader.getFile().getName();
-        this.fitWidth = image.getWidth();
-        this.fitHeight = image.getHeight();
-        this.imageView = new ImageView();
-        setNode(imageView);
-    }
-
     public ImageOverlay(Image image, double x, double y) {
         this.image = image;
         this.fitWidth = image.getWidth();

From d6f36bbc30dc0dbc0bc25b3c80c00cc3c0c5d726 Mon Sep 17 00:00:00 2001
From: HackusatePvP 
Date: Wed, 17 Dec 2025 15:31:24 -0600
Subject: [PATCH 47/78] Added more removeElement functions.

---
 src/main/java/me/piitex/engine/Renderer.java | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/main/java/me/piitex/engine/Renderer.java b/src/main/java/me/piitex/engine/Renderer.java
index 71a536b..3db6fb8 100644
--- a/src/main/java/me/piitex/engine/Renderer.java
+++ b/src/main/java/me/piitex/engine/Renderer.java
@@ -248,6 +248,14 @@ public void removeElement(Element element) {
         }
     }
 
+    public void removeFirstElement() {
+        removeElement(elements.firstKey());
+    }
+
+    public void removeLastElement() {
+        removeElement(elements.lastKey());
+    }
+
     public void removeAllElement(Element element) {
         LinkedHashMap toRemove = new LinkedHashMap<>(elements);
         toRemove.forEach((integer, e) -> {

From 5f6e1b02cadcc2a4a91ece28dbe6fcb478c028c5 Mon Sep 17 00:00:00 2001
From: HackusatePvP 
Date: Wed, 17 Dec 2025 15:31:43 -0600
Subject: [PATCH 48/78] Styling will be applied automatically to the node.

---
 src/main/java/me/piitex/engine/Renderer.java | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/java/me/piitex/engine/Renderer.java b/src/main/java/me/piitex/engine/Renderer.java
index 3db6fb8..03a0cc6 100644
--- a/src/main/java/me/piitex/engine/Renderer.java
+++ b/src/main/java/me/piitex/engine/Renderer.java
@@ -150,6 +150,7 @@ public List getStyles() {
 
     public void addStyle(String style) {
         styles.add(style);
+        getNode().getStyleClass().add(style);
     }
 
     public Window getWindow() {

From 6a07cea756b92c42ad439d00efdb6746daea5a35 Mon Sep 17 00:00:00 2001
From: HackusatePvP 
Date: Tue, 30 Dec 2025 18:54:38 -0600
Subject: [PATCH 49/78] Removed empty mouse handler.

---
 src/main/java/me/piitex/engine/overlays/HyperLinkOverlay.java | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/main/java/me/piitex/engine/overlays/HyperLinkOverlay.java b/src/main/java/me/piitex/engine/overlays/HyperLinkOverlay.java
index 8d84d4c..068b530 100644
--- a/src/main/java/me/piitex/engine/overlays/HyperLinkOverlay.java
+++ b/src/main/java/me/piitex/engine/overlays/HyperLinkOverlay.java
@@ -55,9 +55,6 @@ public Node render() {
         hyperlink.setPadding(new Insets(4,0,4,0));
         hyperlink.setTranslateX(getX());
         hyperlink.setTranslateY(getY());
-        hyperlink.setOnMouseClicked(event -> {
-
-        });
         return hyperlink;
     }
 }

From 7154b684e3041992c187b05d14a16d2d426d6a03 Mon Sep 17 00:00:00 2001
From: HackusatePvP 
Date: Tue, 30 Dec 2025 18:55:21 -0600
Subject: [PATCH 50/78] Fixed possible race condition when the submit handler
 was set before the overlay was rendered.

---
 .../engine/overlays/RichTextAreaOverlay.java  | 24 ++++++++-----------
 1 file changed, 10 insertions(+), 14 deletions(-)

diff --git a/src/main/java/me/piitex/engine/overlays/RichTextAreaOverlay.java b/src/main/java/me/piitex/engine/overlays/RichTextAreaOverlay.java
index 3fdae77..db75317 100644
--- a/src/main/java/me/piitex/engine/overlays/RichTextAreaOverlay.java
+++ b/src/main/java/me/piitex/engine/overlays/RichTextAreaOverlay.java
@@ -166,6 +166,16 @@ public ITextAreaSubmit getiOverlaySubmit() {
 
     public void onSubmit(ITextAreaSubmit iOverlaySubmit) {
         this.iOverlaySubmit = iOverlaySubmit;
+        textArea.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
+            if (keyEvent.getCode() == KeyCode.ENTER) {
+                keyEvent.consume();
+                if (keyEvent.isShiftDown()) {
+                    textArea.insertText(textArea.getCaretPosition(), "\n");
+                } else {
+                    getiOverlaySubmit().onSubmit(new TextAreaSubmitEvent(this, textArea.getText()));
+                }
+            }
+        });
     }
 
     @Override
@@ -305,20 +315,6 @@ public Node render() {
             }, 200, TimeUnit.MILLISECONDS);
         }
 
-        if (getiOverlaySubmit() != null) {
-            textArea.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
-                if (keyEvent.getCode() == KeyCode.ENTER) {
-                    keyEvent.consume();
-                    if (keyEvent.isShiftDown()) {
-                        textArea.insertText(textArea.getCaretPosition(), "\n");
-                    } else {
-                        getiOverlaySubmit().onSubmit(new TextAreaSubmitEvent(this, textArea.getText()));
-                    }
-                }
-            });
-        }
-
-
         return textArea;
     }
 

From a2c4f80ed769749185335ecc1ce1333de233769c Mon Sep 17 00:00:00 2001
From: HackusatePvP 
Date: Tue, 30 Dec 2025 18:55:38 -0600
Subject: [PATCH 51/78] Cleaned up TextAreaOverlay constructors.

---
 .../engine/overlays/TextAreaOverlay.java       | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/main/java/me/piitex/engine/overlays/TextAreaOverlay.java b/src/main/java/me/piitex/engine/overlays/TextAreaOverlay.java
index c887cbc..bcd76e8 100644
--- a/src/main/java/me/piitex/engine/overlays/TextAreaOverlay.java
+++ b/src/main/java/me/piitex/engine/overlays/TextAreaOverlay.java
@@ -22,14 +22,16 @@ public class TextAreaOverlay extends Overlay implements Region {
     private IInputSetEvent iInputSetEvent;
     private ITextAreaSubmit iTextAreaSubmit;
 
-    public TextAreaOverlay(String defaultInput, double x, double y, double width, double height) {
-        this.textArea = new TextArea(defaultInput);
-        this.defaultInput = defaultInput;
-        this.width = width;
-        this.height = height;
-        setNode(textArea);
-        setX(x);
-        setY(y);
+    public TextAreaOverlay(String defaultInput) {
+        this(defaultInput, "", 0, 0, 0, 0);
+    }
+
+    public TextAreaOverlay(String defaultInput, String hintText) {
+        this(defaultInput, hintText, 0, 0, 0, 0);
+    }
+
+    public TextAreaOverlay(String defaultInput, String hintText, double width, double height) {
+        this(defaultInput, hintText, 0, 0, width, height);
     }
 
     public TextAreaOverlay(String defaultInput, String hintText, double x, double y, double width, double height) {

From c0d1166440a89ce067a0121709665a21d8dc6e9a Mon Sep 17 00:00:00 2001
From: HackusatePvP 
Date: Tue, 30 Dec 2025 18:55:58 -0600
Subject: [PATCH 52/78] Added text alignment parameter.

---
 .../engine/overlays/TextFlowOverlay.java      | 35 ++++++++++++-------
 1 file changed, 23 insertions(+), 12 deletions(-)

diff --git a/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java b/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java
index b97637e..def034c 100644
--- a/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java
+++ b/src/main/java/me/piitex/engine/overlays/TextFlowOverlay.java
@@ -6,6 +6,7 @@
 import javafx.scene.paint.Color;
 import javafx.scene.text.FontSmoothingType;
 import javafx.scene.text.Text;
+import javafx.scene.text.TextAlignment;
 import javafx.scene.text.TextFlow;
 import me.piitex.engine.loaders.FontLoader;
 import org.slf4j.Logger;
@@ -20,42 +21,40 @@ public class TextFlowOverlay extends Overlay implements Region {
     private Color textFillColor;
     private FontLoader font;
     private FontSmoothingType fontSmoothingType = FontSmoothingType.GRAY;
+    private TextAlignment textAlignment;
     private double width, height, prefWidth, prefHeight, maxWidth, maxHeight;
     private static final Logger logger = LoggerFactory.getLogger(TextFlowOverlay.class);
 
     public TextFlowOverlay(String text, double width, double height) {
-        this.textFlow = new TextFlow();
-        this.width = width;
-        this.height = height;
-        this.text = text;
-        setNode(textFlow);
+       this(text, null, width, height);
     }
 
-    public TextFlowOverlay(String text, FontLoader fontLoader, double width, double height) {
+    public TextFlowOverlay(TextOverlay text, double width, double height) {
         this.textFlow = new TextFlow();
-        this.texts.add(new TextOverlay(text));
-        this.font = fontLoader;
         this.width = width;
         this.height = height;
+        texts.add(text);
         setNode(textFlow);
     }
 
-    public TextFlowOverlay(TextOverlay text, double width, double height) {
+    public TextFlowOverlay(LinkedList texts, double width, double height) {
         this.textFlow = new TextFlow();
         this.width = width;
         this.height = height;
-        texts.add(text);
+        this.texts = texts;
         setNode(textFlow);
     }
 
-    public TextFlowOverlay(LinkedList texts, double width, double height) {
+    public TextFlowOverlay(String text, FontLoader fontLoader, double width, double height) {
         this.textFlow = new TextFlow();
+        this.font = fontLoader;
         this.width = width;
         this.height = height;
-        this.texts = texts;
         setNode(textFlow);
+        setText(text);
     }
 
+
     public LinkedList getTexts() {
         return texts;
     }
@@ -122,6 +121,14 @@ public void setText(String text) {
         }
     }
 
+    public TextAlignment getTextAlignment() {
+        return textAlignment;
+    }
+
+    public void setTextAlignment(TextAlignment textAlignment) {
+        this.textAlignment = textAlignment;
+    }
+
     public TextFlow getTextFlow() {
         return textFlow;
     }
@@ -131,6 +138,10 @@ public Node render() {
         // Creates text that overflows over the box.
         textFlow.getChildren().clear();
 
+        if (textAlignment != null) {
+            textFlow.setTextAlignment(textAlignment);
+        }
+
         if (text != null) {
             setText(text);
         }

From 053e2cd9c21f69a2963a8c428167aad18f68dfc0 Mon Sep 17 00:00:00 2001
From: HackusatePvP 
Date: Tue, 30 Dec 2025 18:57:38 -0600
Subject: [PATCH 53/78] Pane will request the layout to be recalculated after
 adding the node to view.

---
 src/main/java/me/piitex/engine/Renderer.java | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/java/me/piitex/engine/Renderer.java b/src/main/java/me/piitex/engine/Renderer.java
index 03a0cc6..59ceb10 100644
--- a/src/main/java/me/piitex/engine/Renderer.java
+++ b/src/main/java/me/piitex/engine/Renderer.java
@@ -298,6 +298,7 @@ public void addToView(Node node, int index) {
                     // Fallback for an invalid index
                     pane.getChildren().add(node);
                 }
+                pane.requestLayout();
             } else {
                 pane.getChildren().remove(node);
 

From 0603d0783a609d1ec90b8a0f22f385d3d3b8dfa6 Mon Sep 17 00:00:00 2001
From: HackusatePvP 
Date: Tue, 30 Dec 2025 18:59:00 -0600
Subject: [PATCH 54/78] Added documentation for adding pre-rendered nodes to
 container.

---
 src/main/java/me/piitex/engine/Window.java | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/src/main/java/me/piitex/engine/Window.java b/src/main/java/me/piitex/engine/Window.java
index 7bf0ded..9d84911 100644
--- a/src/main/java/me/piitex/engine/Window.java
+++ b/src/main/java/me/piitex/engine/Window.java
@@ -344,7 +344,26 @@ public void addContainer(Container container, int index) {
     }
 
     /**
-     * Adds a pre-compiled {@link Container} to the window. Use {@link Container#assemble()} to build the {@link Node}.
+     * Adds a pre-compiled {@link Container} to the window. Use {@link Container#assemble()} to build the {@link Node}. Nodes are automatically assembled when the base container is drawn to the screen.
+     * If a Container is large or executes long task it might freeze or lock the UI. You can assemble the Container asynchronously to prevent the UI from freezing.
+     *
+     * 
+     *     {@code
+     *     Container container = new EmptyContainer(100, 100);
+     *     // add elements to the container.
+     *     runTaskAsynchronously(() -> {
+     *         Node assemble = container.assemble();
+     *         // To draw nodes to the screen it must be called in the JavaFX thread.
+     *         Platform.runLater(() -> {
+     *          window.addContainer(container, assemble, 0); // Specifying the index is optional.
+     *         });
+     *     });
+     *
+     *     // Display a loading view which can be removed in the task above.
+     *     }
+     * 
+ * + *
* @param container The container to add. * @param node The pre-compiled node to add. * @param index The desired rendering index for the container. From 5a5a78f8474ac8c786607e77bc452db5ed29dd81 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 30 Dec 2025 18:59:24 -0600 Subject: [PATCH 55/78] Added oshi-core. --- pom.xml | 6 ++++++ src/main/java/me/piitex/os/OSUtil.java | 11 +++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 92bb747..f2df86b 100644 --- a/pom.xml +++ b/pom.xml @@ -130,6 +130,12 @@ 4.10.4 + + com.github.oshi + oshi-core + 6.9.1 + + diff --git a/src/main/java/me/piitex/os/OSUtil.java b/src/main/java/me/piitex/os/OSUtil.java index 8439e0a..0d84a8c 100644 --- a/src/main/java/me/piitex/os/OSUtil.java +++ b/src/main/java/me/piitex/os/OSUtil.java @@ -1,17 +1,16 @@ package me.piitex.os; -import org.apache.commons.lang3.SystemUtils; +import oshi.SystemInfo; +import oshi.software.os.OperatingSystem; /** * Simple utility for gathering operating system information. */ public class OSUtil { + private static final SystemInfo SYSTEM_INFO = new SystemInfo(); + private static final OperatingSystem OS = SYSTEM_INFO.getOperatingSystem(); public static String getOS() { - return SystemUtils.OS_NAME; - } - - public static String getVersion() { - return SystemUtils.OS_VERSION; + return OS.toString(); // Returns something like "Ubuntu 22.04.1 LTS" } } From 1a5affeab255d5b265cd80a80e0a746598dedbf3 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 30 Dec 2025 18:59:52 -0600 Subject: [PATCH 56/78] Fixed versioning scheme returning null if matcher didn't pass. --- src/main/java/me/piitex/os/VersionUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/piitex/os/VersionUtil.java b/src/main/java/me/piitex/os/VersionUtil.java index 194ed9b..2233a16 100644 --- a/src/main/java/me/piitex/os/VersionUtil.java +++ b/src/main/java/me/piitex/os/VersionUtil.java @@ -30,7 +30,7 @@ public static Version parseVersion(String version) { String suffix = matcher.group(3); return new Version(prefix, numericalValue, suffix); } else { - return null; + return new Version("", Integer.parseInt(version), ""); } } } From a053c9741448e68a8314c5a7bdc5c22e2dfbb40f Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Sat, 3 Jan 2026 22:59:59 -0600 Subject: [PATCH 57/78] Moved mouse events to Element class. --- src/main/java/me/piitex/engine/Element.java | 73 ++++++++++++++- src/main/java/me/piitex/engine/Window.java | 7 +- .../engine/containers/BorderContainer.java | 4 - .../engine/containers/CardContainer.java | 6 -- .../piitex/engine/containers/Container.java | 10 --- .../engine/containers/EmptyContainer.java | 4 - .../engine/containers/StackContainer.java | 4 - .../containers/handlers/IContainerClick.java | 8 -- ...ClickEvent.java => ElementClickEvent.java} | 88 +++++++++---------- .../events/ElementClickReleaseEvent.java | 23 +++++ .../hanlders/events/ElementExitEvent.java | 23 +++++ .../hanlders/events/ElementHoverEvent.java | 23 +++++ .../events/OverlayClickReleaseEvent.java | 22 ----- .../hanlders/events/OverlayExitEvent.java | 22 ----- .../hanlders/events/OverlayHoverEvent.java | 22 ----- .../me/piitex/engine/layouts/FlowLayout.java | 6 -- .../engine/layouts/HorizontalLayout.java | 6 -- .../java/me/piitex/engine/layouts/Layout.java | 10 --- .../piitex/engine/layouts/TitledLayout.java | 10 --- .../piitex/engine/layouts/VerticalLayout.java | 8 -- .../layouts/handles/ILayoutClickEvent.java | 8 -- .../me/piitex/engine/overlays/BoxOverlay.java | 4 - .../me/piitex/engine/overlays/Overlay.java | 69 +-------------- .../engine/overlays/SeparatorOverlay.java | 1 - .../engine/overlays/events/IOverlayClick.java | 4 +- .../overlays/events/IOverlayClickRelease.java | 4 +- .../engine/overlays/events/IOverlayHover.java | 4 +- .../overlays/events/IOverlayHoverExit.java | 4 +- 28 files changed, 199 insertions(+), 278 deletions(-) delete mode 100644 src/main/java/me/piitex/engine/containers/handlers/IContainerClick.java rename src/main/java/me/piitex/engine/hanlders/events/{OverlayClickEvent.java => ElementClickEvent.java} (69%) create mode 100644 src/main/java/me/piitex/engine/hanlders/events/ElementClickReleaseEvent.java create mode 100644 src/main/java/me/piitex/engine/hanlders/events/ElementExitEvent.java create mode 100644 src/main/java/me/piitex/engine/hanlders/events/ElementHoverEvent.java delete mode 100644 src/main/java/me/piitex/engine/hanlders/events/OverlayClickReleaseEvent.java delete mode 100644 src/main/java/me/piitex/engine/hanlders/events/OverlayExitEvent.java delete mode 100644 src/main/java/me/piitex/engine/hanlders/events/OverlayHoverEvent.java delete mode 100644 src/main/java/me/piitex/engine/layouts/handles/ILayoutClickEvent.java diff --git a/src/main/java/me/piitex/engine/Element.java b/src/main/java/me/piitex/engine/Element.java index cd94db7..dcca87c 100644 --- a/src/main/java/me/piitex/engine/Element.java +++ b/src/main/java/me/piitex/engine/Element.java @@ -1,16 +1,22 @@ package me.piitex.engine; +import javafx.scene.Cursor; import javafx.scene.Node; +import javafx.scene.input.MouseEvent; import me.piitex.engine.containers.Container; import me.piitex.engine.exceptions.NodeNotDefinedException; +import me.piitex.engine.hanlders.events.*; import me.piitex.engine.layouts.Layout; import me.piitex.engine.overlays.ImageOverlay; import me.piitex.engine.overlays.Overlay; import me.piitex.engine.overlays.TextOverlay; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.function.Consumer; + /** * Represents a graphical element that can be rendered to the {@link Window} or a {@link Container}. *

@@ -26,7 +32,12 @@ public abstract class Element { private int index = 0; private boolean enabled = true; - private Node node; // Underlying JavaFX component + @Nullable private Node node; // Underlying JavaFX component + private Cursor cursor; + private Consumer hoverConsumer; + private Consumer clickConsumer; + private Consumer mouseExitConsumer; + private Consumer clickReleaseConsumer; private static final Logger logger = LoggerFactory.getLogger(Element.class); @@ -74,6 +85,65 @@ public void setNode(Node node) { this.node = node; } + public Cursor getCursor() { + return cursor; + } + + public void setCursor(Cursor cursor) { + this.cursor = cursor; + if (node != null) { + node.setCursor(cursor); + } + } + + public void onClick(Consumer clickConsumer) { + this.clickConsumer = clickConsumer; + + if (node != null) { + if (clickConsumer != null) { + node.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEvent -> { + clickConsumer.accept(new ElementClickEvent(this, mouseEvent, mouseEvent.getSceneX(), mouseEvent.getSceneY())); + }); + } + } + } + + public void onClickRelease(Consumer clickReleaseConsumer) { + this.clickReleaseConsumer = clickReleaseConsumer; + + if (node != null) { + if (clickReleaseConsumer != null) { + node.addEventHandler(MouseEvent.MOUSE_RELEASED, mouseEvent -> { + clickReleaseConsumer.accept(new ElementClickReleaseEvent(this, mouseEvent)); + }); + } + } + } + + public void onHover(Consumer hoverConsumer) { + this.hoverConsumer = hoverConsumer; + + if (node != null) { + if (hoverConsumer != null) { + node.addEventHandler(MouseEvent.MOUSE_ENTERED, mouseEvent -> { + hoverConsumer.accept(new ElementHoverEvent(this, mouseEvent)); + }); + } + } + } + + public void onMouseExit(Consumer mouseExitConsumer) { + this.mouseExitConsumer = mouseExitConsumer; + + if (node != null) { + if (mouseExitConsumer != null) { + node.addEventHandler(MouseEvent.MOUSE_EXITED, mouseEvent -> { + mouseExitConsumer.accept(new ElementExitEvent(this, mouseEvent)); + }); + } + } + } + /** * Assembles the element into its JavaFX {@link Node}. * @@ -89,4 +159,5 @@ public void setNode(Node node) { * @return The constructed node. */ public abstract Node assemble(); + } diff --git a/src/main/java/me/piitex/engine/Window.java b/src/main/java/me/piitex/engine/Window.java index 9d84911..47957b7 100644 --- a/src/main/java/me/piitex/engine/Window.java +++ b/src/main/java/me/piitex/engine/Window.java @@ -161,7 +161,7 @@ protected void buildStage() { if (OSUtil.getOS().contains("Windows")) { stage.setHeight(height + 40); } else if (OSUtil.getOS().contains("Linux")) { - stage.setHeight(height); + stage.setHeight(height + 35); } else { stage.setHeight(height); } @@ -184,8 +184,11 @@ protected void buildStage() { handleWindowScaling(stage); scene = new Scene(root); - stage.setScene(scene); + + if (backgroundColor != null) { + updateBackground(backgroundColor); + } } /** diff --git a/src/main/java/me/piitex/engine/containers/BorderContainer.java b/src/main/java/me/piitex/engine/containers/BorderContainer.java index 7aa1f0a..f5f2447 100644 --- a/src/main/java/me/piitex/engine/containers/BorderContainer.java +++ b/src/main/java/me/piitex/engine/containers/BorderContainer.java @@ -104,10 +104,6 @@ public Node build() { setStyling(pane); - if (getOnClick() != null) { - pane.setOnMouseClicked(event -> getOnClick().onClick(new ContainerClickEvent(this))); - } - return pane; } } diff --git a/src/main/java/me/piitex/engine/containers/CardContainer.java b/src/main/java/me/piitex/engine/containers/CardContainer.java index 2646a54..42c1b6c 100644 --- a/src/main/java/me/piitex/engine/containers/CardContainer.java +++ b/src/main/java/me/piitex/engine/containers/CardContainer.java @@ -128,12 +128,6 @@ public Node build() { atlantafxCard.setMaxHeight(getMaxHeight()); } - if (getOnClick() != null) { - atlantafxCard.setOnMouseClicked(mouseEvent -> { - getOnClick().onClick(new ContainerClickEvent(this)); - }); - } - atlantafxCard.getStyleClass().addAll(getStyles()); diff --git a/src/main/java/me/piitex/engine/containers/Container.java b/src/main/java/me/piitex/engine/containers/Container.java index 8cb3be2..eb0ba05 100644 --- a/src/main/java/me/piitex/engine/containers/Container.java +++ b/src/main/java/me/piitex/engine/containers/Container.java @@ -3,7 +3,6 @@ import javafx.scene.Node; import me.piitex.engine.Renderer; import me.piitex.engine.Window; -import me.piitex.engine.containers.handlers.IContainerClick; import me.piitex.engine.containers.handlers.IContainerRender; import me.piitex.engine.layouts.Layout; import me.piitex.engine.overlays.Overlay; @@ -35,7 +34,6 @@ public abstract class Container extends Renderer { private double x, y; private final List stylesheets = new ArrayList<>(); - private IContainerClick click; private final List renderEvents = new LinkedList<>(); public Container(Node view, double x, double y, double width, double height) { @@ -91,14 +89,6 @@ public void setY(double y) { getNode().setTranslateY(y); } - public IContainerClick getOnClick() { - return click; - } - - public void onClick(IContainerClick click) { - this.click = click; - } - public void addRenderEvent(IContainerRender renderEvent) { if (renderEvent != null) { this.renderEvents.add(renderEvent); diff --git a/src/main/java/me/piitex/engine/containers/EmptyContainer.java b/src/main/java/me/piitex/engine/containers/EmptyContainer.java index 242e325..0c1acb4 100644 --- a/src/main/java/me/piitex/engine/containers/EmptyContainer.java +++ b/src/main/java/me/piitex/engine/containers/EmptyContainer.java @@ -74,10 +74,6 @@ public Node build() { setStyling(pane); - if (getOnClick() != null) { - pane.setOnMouseClicked(event -> getOnClick().onClick(new ContainerClickEvent(this))); - } - return pane; } } \ No newline at end of file diff --git a/src/main/java/me/piitex/engine/containers/StackContainer.java b/src/main/java/me/piitex/engine/containers/StackContainer.java index 939372a..b72ac76 100644 --- a/src/main/java/me/piitex/engine/containers/StackContainer.java +++ b/src/main/java/me/piitex/engine/containers/StackContainer.java @@ -44,10 +44,6 @@ public Node build() { setStyling(pane); - if (getOnClick() != null) { - pane.setOnMouseClicked(event -> getOnClick().onClick(new ContainerClickEvent(this))); - } - return pane; } } diff --git a/src/main/java/me/piitex/engine/containers/handlers/IContainerClick.java b/src/main/java/me/piitex/engine/containers/handlers/IContainerClick.java deleted file mode 100644 index 4215741..0000000 --- a/src/main/java/me/piitex/engine/containers/handlers/IContainerClick.java +++ /dev/null @@ -1,8 +0,0 @@ -package me.piitex.engine.containers.handlers; - -import me.piitex.engine.hanlders.events.ContainerClickEvent; - -public interface IContainerClick { - - void onClick(ContainerClickEvent event); -} diff --git a/src/main/java/me/piitex/engine/hanlders/events/OverlayClickEvent.java b/src/main/java/me/piitex/engine/hanlders/events/ElementClickEvent.java similarity index 69% rename from src/main/java/me/piitex/engine/hanlders/events/OverlayClickEvent.java rename to src/main/java/me/piitex/engine/hanlders/events/ElementClickEvent.java index 56d539c..6eff3a0 100644 --- a/src/main/java/me/piitex/engine/hanlders/events/OverlayClickEvent.java +++ b/src/main/java/me/piitex/engine/hanlders/events/ElementClickEvent.java @@ -1,44 +1,44 @@ -package me.piitex.engine.hanlders.events; - -import javafx.scene.input.MouseButton; -import javafx.scene.input.MouseEvent; -import me.piitex.engine.overlays.Overlay; - -public class OverlayClickEvent extends Event { - private final Overlay overlay; - private final MouseEvent event; - private final double x, y; - - - public OverlayClickEvent(Overlay overlay, MouseEvent event, double x, double y) { - this.overlay = overlay; - this.event = event; - this.x = x; - this.y = y; - } - - public Overlay getOverlay() { - return overlay; - } - - - public double getX() { - return x; - } - - public double getY() { - return y; - } - - public MouseEvent getHandler() { - return event; - } - - public boolean isRightClicked() { - return event.getButton() == MouseButton.SECONDARY; - } - - public boolean isMiddleButton() { - return event.getButton() == MouseButton.MIDDLE; - } -} +package me.piitex.engine.hanlders.events; + +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import me.piitex.engine.Element; +import me.piitex.engine.overlays.Overlay; + +public class ElementClickEvent extends Event { + private final Element element; + private final MouseEvent event; + private final double x, y; + + + public ElementClickEvent(Element element, MouseEvent event, double x, double y) { + this.element = element; + this.event = event; + this.x = x; + this.y = y; + } + + public Element getElement() { + return element; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public MouseEvent getHandler() { + return event; + } + + public boolean isRightClicked() { + return event.getButton() == MouseButton.SECONDARY; + } + + public boolean isMiddleButton() { + return event.getButton() == MouseButton.MIDDLE; + } +} diff --git a/src/main/java/me/piitex/engine/hanlders/events/ElementClickReleaseEvent.java b/src/main/java/me/piitex/engine/hanlders/events/ElementClickReleaseEvent.java new file mode 100644 index 0000000..f70356a --- /dev/null +++ b/src/main/java/me/piitex/engine/hanlders/events/ElementClickReleaseEvent.java @@ -0,0 +1,23 @@ +package me.piitex.engine.hanlders.events; + +import javafx.scene.input.MouseEvent; +import me.piitex.engine.Element; +import me.piitex.engine.overlays.Overlay; + +public class ElementClickReleaseEvent extends Event { + private final Element element; + private final MouseEvent event; + + public ElementClickReleaseEvent(Element element, MouseEvent event) { + this.element = element; + this.event = event; + } + + public Element getElement() { + return element; + } + + public MouseEvent getHandler() { + return event; + } +} diff --git a/src/main/java/me/piitex/engine/hanlders/events/ElementExitEvent.java b/src/main/java/me/piitex/engine/hanlders/events/ElementExitEvent.java new file mode 100644 index 0000000..1a9c321 --- /dev/null +++ b/src/main/java/me/piitex/engine/hanlders/events/ElementExitEvent.java @@ -0,0 +1,23 @@ +package me.piitex.engine.hanlders.events; + +import javafx.scene.input.MouseEvent; +import me.piitex.engine.Element; +import me.piitex.engine.overlays.Overlay; + +public class ElementExitEvent extends Event { + private final Element element; + private final MouseEvent event; + + public ElementExitEvent(Element element, MouseEvent event) { + this.element = element; + this.event = event; + } + + public Element getElement() { + return element; + } + + public MouseEvent getHandler() { + return event; + } +} diff --git a/src/main/java/me/piitex/engine/hanlders/events/ElementHoverEvent.java b/src/main/java/me/piitex/engine/hanlders/events/ElementHoverEvent.java new file mode 100644 index 0000000..5192c36 --- /dev/null +++ b/src/main/java/me/piitex/engine/hanlders/events/ElementHoverEvent.java @@ -0,0 +1,23 @@ +package me.piitex.engine.hanlders.events; + +import javafx.scene.input.MouseEvent; +import me.piitex.engine.Element; +import me.piitex.engine.overlays.Overlay; + +public class ElementHoverEvent extends Event { + private final Element element; + private final MouseEvent mouseEvent; + + public ElementHoverEvent(Element element, MouseEvent event) { + this.element = element; + this.mouseEvent = event; + } + + public Element getElement() { + return element; + } + + public MouseEvent getHandler() { + return mouseEvent; + } +} diff --git a/src/main/java/me/piitex/engine/hanlders/events/OverlayClickReleaseEvent.java b/src/main/java/me/piitex/engine/hanlders/events/OverlayClickReleaseEvent.java deleted file mode 100644 index 6147228..0000000 --- a/src/main/java/me/piitex/engine/hanlders/events/OverlayClickReleaseEvent.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.piitex.engine.hanlders.events; - -import javafx.scene.input.MouseEvent; -import me.piitex.engine.overlays.Overlay; - -public class OverlayClickReleaseEvent extends Event { - private final Overlay overlay; - private final MouseEvent event; - - public OverlayClickReleaseEvent(Overlay overlay, MouseEvent event) { - this.overlay = overlay; - this.event = event; - } - - public Overlay getOverlay() { - return overlay; - } - - public MouseEvent getHandler() { - return event; - } -} diff --git a/src/main/java/me/piitex/engine/hanlders/events/OverlayExitEvent.java b/src/main/java/me/piitex/engine/hanlders/events/OverlayExitEvent.java deleted file mode 100644 index 1c5edf1..0000000 --- a/src/main/java/me/piitex/engine/hanlders/events/OverlayExitEvent.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.piitex.engine.hanlders.events; - -import javafx.scene.input.MouseEvent; -import me.piitex.engine.overlays.Overlay; - -public class OverlayExitEvent extends Event { - private final Overlay overlay; - private final MouseEvent event; - - public OverlayExitEvent(Overlay overlay, MouseEvent event) { - this.overlay = overlay; - this.event = event; - } - - public Overlay getOverlay() { - return overlay; - } - - public MouseEvent getHandler() { - return event; - } -} diff --git a/src/main/java/me/piitex/engine/hanlders/events/OverlayHoverEvent.java b/src/main/java/me/piitex/engine/hanlders/events/OverlayHoverEvent.java deleted file mode 100644 index 2624f52..0000000 --- a/src/main/java/me/piitex/engine/hanlders/events/OverlayHoverEvent.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.piitex.engine.hanlders.events; - -import javafx.scene.input.MouseEvent; -import me.piitex.engine.overlays.Overlay; - -public class OverlayHoverEvent extends Event { - private final Overlay overlay; - private final MouseEvent mouseEvent; - - public OverlayHoverEvent(Overlay overlay, MouseEvent event) { - this.overlay = overlay; - this.mouseEvent = event; - } - - public Overlay getOverlay() { - return overlay; - } - - public MouseEvent getHandler() { - return mouseEvent; - } -} diff --git a/src/main/java/me/piitex/engine/layouts/FlowLayout.java b/src/main/java/me/piitex/engine/layouts/FlowLayout.java index 7b25933..fdf608d 100644 --- a/src/main/java/me/piitex/engine/layouts/FlowLayout.java +++ b/src/main/java/me/piitex/engine/layouts/FlowLayout.java @@ -72,12 +72,6 @@ public Node render() { pane.setBackground(new Background(new BackgroundFill(getBackgroundColor(), CornerRadii.EMPTY, Insets.EMPTY))); } - if (getClickEvent() != null) { - pane.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEvent -> { - getClickEvent().onLayoutClick(new LayoutClickEvent(mouseEvent,this)); - }); - } - setStyling(pane); return pane; } diff --git a/src/main/java/me/piitex/engine/layouts/HorizontalLayout.java b/src/main/java/me/piitex/engine/layouts/HorizontalLayout.java index 6de9a2d..2d38b65 100644 --- a/src/main/java/me/piitex/engine/layouts/HorizontalLayout.java +++ b/src/main/java/me/piitex/engine/layouts/HorizontalLayout.java @@ -60,12 +60,6 @@ public Node render() { pane.setBackground(new Background(new BackgroundFill(getBackgroundColor(), CornerRadii.EMPTY, Insets.EMPTY))); } - if (getClickEvent() != null) { - pane.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEvent -> { - getClickEvent().onLayoutClick(new LayoutClickEvent(mouseEvent,this)); - }); - } - setStyling(pane); return pane; } diff --git a/src/main/java/me/piitex/engine/layouts/Layout.java b/src/main/java/me/piitex/engine/layouts/Layout.java index 6fab7bb..1eb3750 100644 --- a/src/main/java/me/piitex/engine/layouts/Layout.java +++ b/src/main/java/me/piitex/engine/layouts/Layout.java @@ -5,7 +5,6 @@ import javafx.scene.Node; import javafx.scene.layout.Pane; import me.piitex.engine.Renderer; -import me.piitex.engine.layouts.handles.ILayoutClickEvent; import me.piitex.engine.layouts.handles.ILayoutRender; import java.util.ArrayList; @@ -16,7 +15,6 @@ public abstract class Layout extends Renderer { private double x, y; private Insets padding; private Pos alignment; - private ILayoutClickEvent clickEvent; private final List renderEvents; protected Layout(Pane pane, double width, double height) { @@ -27,14 +25,6 @@ protected Layout(Pane pane, double width, double height) { this.renderEvents = new ArrayList<>(); } - public ILayoutClickEvent getClickEvent() { - return clickEvent; - } - - public void setClickEvent(ILayoutClickEvent clickEvent) { - this.clickEvent = clickEvent; - } - public void addRenderEvent(ILayoutRender renderEvent) { if (renderEvent != null) { this.renderEvents.add(renderEvent); diff --git a/src/main/java/me/piitex/engine/layouts/TitledLayout.java b/src/main/java/me/piitex/engine/layouts/TitledLayout.java index b56a649..009bd85 100644 --- a/src/main/java/me/piitex/engine/layouts/TitledLayout.java +++ b/src/main/java/me/piitex/engine/layouts/TitledLayout.java @@ -100,16 +100,6 @@ public Node render() { } } - - if (getClickEvent() != null) { - pane.setOnMousePressed(mouseEvent -> { - getClickEvent().onLayoutClick(new LayoutClickEvent(mouseEvent, this)); - }); - } else { - pane.setOnMousePressed(null); - } - - setStyling(pane); titledPane.setContent(pane); return titledPane; diff --git a/src/main/java/me/piitex/engine/layouts/VerticalLayout.java b/src/main/java/me/piitex/engine/layouts/VerticalLayout.java index f51f574..e29a9db 100644 --- a/src/main/java/me/piitex/engine/layouts/VerticalLayout.java +++ b/src/main/java/me/piitex/engine/layouts/VerticalLayout.java @@ -57,14 +57,6 @@ public Node render() { pane.setBackground(new Background(new BackgroundFill(getBackgroundColor(), CornerRadii.EMPTY, Insets.EMPTY))); } - if (getClickEvent() != null) { - pane.setOnMousePressed(mouseEvent -> { - getClickEvent().onLayoutClick(new LayoutClickEvent(mouseEvent, this)); - }); - } else { - pane.setOnMousePressed(null); - } - setStyling(pane); return pane; } diff --git a/src/main/java/me/piitex/engine/layouts/handles/ILayoutClickEvent.java b/src/main/java/me/piitex/engine/layouts/handles/ILayoutClickEvent.java deleted file mode 100644 index 26e8207..0000000 --- a/src/main/java/me/piitex/engine/layouts/handles/ILayoutClickEvent.java +++ /dev/null @@ -1,8 +0,0 @@ -package me.piitex.engine.layouts.handles; - -import me.piitex.engine.hanlders.events.LayoutClickEvent; - -public interface ILayoutClickEvent { - - void onLayoutClick(LayoutClickEvent event); -} diff --git a/src/main/java/me/piitex/engine/overlays/BoxOverlay.java b/src/main/java/me/piitex/engine/overlays/BoxOverlay.java index bb571e0..5eb0636 100644 --- a/src/main/java/me/piitex/engine/overlays/BoxOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/BoxOverlay.java @@ -133,11 +133,7 @@ public Rectangle getRectangle() { public Node render() { rectangle.setX(getX()); rectangle.setY(getY()); - if (getFillColor() != null) { - rectangle.setFill(getFillColor()); - } rectangle.setStroke(strokeColor); - return rectangle; } } diff --git a/src/main/java/me/piitex/engine/overlays/Overlay.java b/src/main/java/me/piitex/engine/overlays/Overlay.java index 61dd296..1372b64 100644 --- a/src/main/java/me/piitex/engine/overlays/Overlay.java +++ b/src/main/java/me/piitex/engine/overlays/Overlay.java @@ -21,7 +21,7 @@ /** * An overlay is a visual element which can be rendered. The overlay class is the JavaFX equivalent of a {@link Node}. - * All overlays have generic events that are fired. For example, the {@link OverlayClickEvent} is fired if the overlay is clicked. + * All overlays have generic events that are fired. For example, the {@link ElementClickEvent} is fired if the overlay is clicked. * *

* To render an overlay you first need to add it to a {@link Container}. The container will have to be managed to a {@link Window}. @@ -105,46 +105,10 @@ public void setTooltip(String tooltip) { this.tooltip = tooltip; } - public void onClick(IOverlayClick iOverlayClick) { - this.iOverlayClick = iOverlayClick; - } - - public void onHover(IOverlayHover iOverlayHover) { - this.iOverlayHover = iOverlayHover; - } - - public void onClickRelease(IOverlayClickRelease iOverlayClickRelease) { - this.iOverlayClickRelease = iOverlayClickRelease; - } - - public void onHoverExit(IOverlayHoverExit iOverlayHoverExit) { - this.iOverlayHoverExit = iOverlayHoverExit; - } - public void onOverlaySubmit(IOverlaySubmit iOverlaySubmit) { this.iOverlaySubmit = iOverlaySubmit; } - public IOverlayClick getOnClick() { - return iOverlayClick; - } - - public IOverlayHover getOnHover() { - return iOverlayHover; - } - - public IOverlayHoverExit getOnHoverExit() { - return iOverlayHoverExit; - } - - public IOverlayClickRelease getOnRelease() { - return iOverlayClickRelease; - } - - public IOverlayClick getiOverlayClick() { - return iOverlayClick; - } - public List getStyleSheets() { return styleSheets; } @@ -218,37 +182,6 @@ public void setInputControls(Node node) { } } - if (node.getOnDragEntered() == null) { - node.setOnMouseEntered(event -> { - //RenJava.getEventHandler().callEvent(new OverlayHoverEvent(this, event)); - if (getOnHover() != null) { - getOnHover().onHover(new OverlayHoverEvent(this, event)); - } - }); - } - if (node.getOnMouseClicked() == null) { - node.setOnMouseClicked(event -> { - OverlayClickEvent overlayClickEvent = new OverlayClickEvent(this, event, event.getSceneX(), event.getSceneY()); - if (getOnClick() != null) { - getOnClick().onClick(overlayClickEvent); - } - }); - } - if (node.getOnMouseExited() == null) { - node.setOnMouseExited(event -> { - if (getOnHoverExit() != null) { - getOnHoverExit().onHoverExit(new OverlayExitEvent(this, event)); - } - }); - } - if (node.getOnMouseReleased() == null) { - node.setOnMouseReleased(event -> { - if (getOnRelease() != null) { - getOnRelease().onClickRelease(new OverlayClickReleaseEvent(this, event)); - } - }); - } - if (node instanceof TextArea textArea) { if (node.getOnKeyPressed() == null) { node.setOnKeyPressed(event -> { diff --git a/src/main/java/me/piitex/engine/overlays/SeparatorOverlay.java b/src/main/java/me/piitex/engine/overlays/SeparatorOverlay.java index 1262917..5e80301 100644 --- a/src/main/java/me/piitex/engine/overlays/SeparatorOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/SeparatorOverlay.java @@ -8,7 +8,6 @@ public class SeparatorOverlay extends Overlay implements Region { private final Separator separator; private Orientation orientation; private double width, height, prefWidth, prefHeight, maxWidth, maxHeight; - private double scaleWidth, scaleHeight; public SeparatorOverlay(Orientation orientation) { this.separator = new Separator(orientation); diff --git a/src/main/java/me/piitex/engine/overlays/events/IOverlayClick.java b/src/main/java/me/piitex/engine/overlays/events/IOverlayClick.java index 90ed213..4656411 100644 --- a/src/main/java/me/piitex/engine/overlays/events/IOverlayClick.java +++ b/src/main/java/me/piitex/engine/overlays/events/IOverlayClick.java @@ -1,8 +1,8 @@ package me.piitex.engine.overlays.events; -import me.piitex.engine.hanlders.events.OverlayClickEvent; +import me.piitex.engine.hanlders.events.ElementClickEvent; public interface IOverlayClick { - void onClick(OverlayClickEvent event); + void onClick(ElementClickEvent event); } diff --git a/src/main/java/me/piitex/engine/overlays/events/IOverlayClickRelease.java b/src/main/java/me/piitex/engine/overlays/events/IOverlayClickRelease.java index 3782f9f..239a038 100644 --- a/src/main/java/me/piitex/engine/overlays/events/IOverlayClickRelease.java +++ b/src/main/java/me/piitex/engine/overlays/events/IOverlayClickRelease.java @@ -1,8 +1,8 @@ package me.piitex.engine.overlays.events; -import me.piitex.engine.hanlders.events.OverlayClickReleaseEvent; +import me.piitex.engine.hanlders.events.ElementClickReleaseEvent; public interface IOverlayClickRelease { - void onClickRelease(OverlayClickReleaseEvent event); + void onClickRelease(ElementClickReleaseEvent event); } diff --git a/src/main/java/me/piitex/engine/overlays/events/IOverlayHover.java b/src/main/java/me/piitex/engine/overlays/events/IOverlayHover.java index 66458d0..d44b565 100644 --- a/src/main/java/me/piitex/engine/overlays/events/IOverlayHover.java +++ b/src/main/java/me/piitex/engine/overlays/events/IOverlayHover.java @@ -1,8 +1,8 @@ package me.piitex.engine.overlays.events; -import me.piitex.engine.hanlders.events.OverlayHoverEvent; +import me.piitex.engine.hanlders.events.ElementHoverEvent; public interface IOverlayHover { - void onHover(OverlayHoverEvent event); + void onHover(ElementHoverEvent event); } diff --git a/src/main/java/me/piitex/engine/overlays/events/IOverlayHoverExit.java b/src/main/java/me/piitex/engine/overlays/events/IOverlayHoverExit.java index e06f1bd..9fca4d8 100644 --- a/src/main/java/me/piitex/engine/overlays/events/IOverlayHoverExit.java +++ b/src/main/java/me/piitex/engine/overlays/events/IOverlayHoverExit.java @@ -1,8 +1,8 @@ package me.piitex.engine.overlays.events; -import me.piitex.engine.hanlders.events.OverlayExitEvent; +import me.piitex.engine.hanlders.events.ElementExitEvent; public interface IOverlayHoverExit { - void onHoverExit(OverlayExitEvent event); + void onHoverExit(ElementExitEvent event); } From 73785316d8b1aba37e25465b7c0996c34efdbed2 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 13 Jan 2026 14:04:54 -0600 Subject: [PATCH 58/78] Fixed casting issues when using a different layout for scroll container. --- .../java/me/piitex/engine/containers/ScrollContainer.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/piitex/engine/containers/ScrollContainer.java b/src/main/java/me/piitex/engine/containers/ScrollContainer.java index 7bfa7ee..fc1afa0 100644 --- a/src/main/java/me/piitex/engine/containers/ScrollContainer.java +++ b/src/main/java/me/piitex/engine/containers/ScrollContainer.java @@ -144,10 +144,13 @@ public Node build() { setStyling(scrollPane); // Build pane layout for the scroll content - VBox pane = (VBox) layout.assemble(); + Pane pane = (Pane) layout.assemble(); LayoutRenderEvent event = new LayoutRenderEvent((Pane) layout.getNode(), layout); layout.getRenderEvents().forEach(iLayoutRender -> iLayoutRender.onLayoutRender(event)); - pane.setAlignment(layout.getAlignment()); + + if (pane instanceof VBox vBox) { + vBox.setAlignment(layout.getAlignment()); + } if (scrollToBottom) { pane.heightProperty().addListener(observable -> { From ca65cfde210452d6b432c73ff1a232053ecea0bf Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 13 Jan 2026 14:05:46 -0600 Subject: [PATCH 59/78] Fixed inconsistent return type for choice box selection. --- src/main/java/me/piitex/engine/overlays/CheckBoxOverlay.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/me/piitex/engine/overlays/CheckBoxOverlay.java b/src/main/java/me/piitex/engine/overlays/CheckBoxOverlay.java index 7436ba8..b36149e 100644 --- a/src/main/java/me/piitex/engine/overlays/CheckBoxOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/CheckBoxOverlay.java @@ -7,7 +7,6 @@ public class CheckBoxOverlay extends Overlay { private final CheckBox checkBox; - private boolean selected; private boolean defaultValue; private String label; @@ -39,11 +38,10 @@ public void setLabel(String label) { } public boolean isSelected() { - return selected; + return checkBox.isSelected(); } public void setSelected(boolean selected) { - this.selected = selected; checkBox.setSelected(selected); } From e289e502ef445664d79f6d7e1f4b8a3e4b41431b Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 13 Jan 2026 14:06:10 -0600 Subject: [PATCH 60/78] Added graphics card information. --- src/main/java/me/piitex/os/OSUtil.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/me/piitex/os/OSUtil.java b/src/main/java/me/piitex/os/OSUtil.java index 0d84a8c..6855e1a 100644 --- a/src/main/java/me/piitex/os/OSUtil.java +++ b/src/main/java/me/piitex/os/OSUtil.java @@ -1,8 +1,11 @@ package me.piitex.os; import oshi.SystemInfo; +import oshi.hardware.GraphicsCard; import oshi.software.os.OperatingSystem; +import java.util.List; + /** * Simple utility for gathering operating system information. */ @@ -13,4 +16,12 @@ public class OSUtil { public static String getOS() { return OS.toString(); // Returns something like "Ubuntu 22.04.1 LTS" } + + public List getGraphicsCards() { + return SYSTEM_INFO.getHardware().getGraphicsCards(); + } + + public long getGraphicsTotalVRAM(GraphicsCard graphicsCard) { + return graphicsCard.getVRam(); + } } From cabc335bd4946087eb0039a91172c75beb38d744 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Wed, 14 Jan 2026 15:38:44 -0600 Subject: [PATCH 61/78] Added getFirstElement and getLastElement. --- src/main/java/me/piitex/engine/Renderer.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/me/piitex/engine/Renderer.java b/src/main/java/me/piitex/engine/Renderer.java index 59ceb10..034e031 100644 --- a/src/main/java/me/piitex/engine/Renderer.java +++ b/src/main/java/me/piitex/engine/Renderer.java @@ -175,6 +175,14 @@ public Element getElementAt(int index) { return elements.get(index); } + public Element getFirstElement() { + return elements.firstEntry().getValue(); + } + + public Element getLastElement() { + return elements.lastEntry().getValue(); + } + /** * 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. From 25984610626ec61062cae3d0f07b2eaa094533e5 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Sun, 18 Jan 2026 18:19:43 -0600 Subject: [PATCH 62/78] Added contains element method. --- src/main/java/me/piitex/engine/Renderer.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/me/piitex/engine/Renderer.java b/src/main/java/me/piitex/engine/Renderer.java index 034e031..e2cb3a2 100644 --- a/src/main/java/me/piitex/engine/Renderer.java +++ b/src/main/java/me/piitex/engine/Renderer.java @@ -297,6 +297,14 @@ public void replaceElement(int index, Element element) { } } + public boolean containsElement(int index) { + return elements.containsKey(index); + } + + public boolean containsElement(Element element) { + return elements.containsValue(element); + } + public void addToView(Node node, int index) { if (getNode() instanceof Pane pane) { if (!pane.getChildren().contains(node)) { From fecc8781b10354d25373d5aec6fadf7a6caa3be4 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Fri, 3 Apr 2026 13:27:58 -0500 Subject: [PATCH 63/78] Added hash checking for downloads. --- .../java/me/piitex/os/DownloadListener.java | 2 +- .../java/me/piitex/os/FileDownloader.java | 123 ++++++++++++------ src/main/java/me/piitex/os/GitHubUtil.java | 87 +++++++++---- 3 files changed, 149 insertions(+), 63 deletions(-) diff --git a/src/main/java/me/piitex/os/DownloadListener.java b/src/main/java/me/piitex/os/DownloadListener.java index a6ae127..71f115a 100644 --- a/src/main/java/me/piitex/os/DownloadListener.java +++ b/src/main/java/me/piitex/os/DownloadListener.java @@ -31,7 +31,7 @@ public interface DownloadListener { void onDownloadError(DownloadInfo info, Exception e); /** - * Called when the download is explicitly cancelled by the user via cancelDownload(url). + * Called when the download is explicitly canceled by the user via cancelDownload(url). * @param info The state of the download at the time of cancellation. */ void onDownloadCancel(DownloadInfo info); diff --git a/src/main/java/me/piitex/os/FileDownloader.java b/src/main/java/me/piitex/os/FileDownloader.java index f46e729..14e7846 100644 --- a/src/main/java/me/piitex/os/FileDownloader.java +++ b/src/main/java/me/piitex/os/FileDownloader.java @@ -10,6 +10,7 @@ import java.net.URI; import java.net.URL; import java.net.URLConnection; +import java.security.MessageDigest; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -22,34 +23,47 @@ public class FileDownloader { private final ConcurrentHashMap activeDownloads = new ConcurrentHashMap<>(); private final ConcurrentMap activeConnections = new ConcurrentHashMap<>(); private final Set listeners = Collections.newSetFromMap(new ConcurrentHashMap<>()); - private final Map requestProperties = new HashMap<>(); + private final ExecutorService executorService; /** * Initializes the FileDownloader and its thread pool. */ public FileDownloader() { - + // Creates a thread pool that reuses previously constructed threads when they are available + this.executorService = Executors.newCachedThreadPool(); } /** - * Starts the download task asynchronously. + * Starts the download task asynchronously without hash checking. * @param fileUrl The URL of the file to download. * @param outputFile The destination of the file. */ public void startDownload(String fileUrl, File outputFile) { + startDownload(fileUrl, outputFile, null, null); + } + + /** + * Starts the download task asynchronously with security hash checking. + * @param fileUrl The URL of the file to download. + * @param outputFile The destination of the file. + * @param expectedHash The expected checksum hash of the file (e.g., SHA-256). + * @param hashAlgorithm The algorithm used for the hash (e.g., "SHA-256", "MD5"). Defaults to SHA-256 if null. + */ + public void startDownload(String fileUrl, File outputFile, String expectedHash, String hashAlgorithm) { logger.info("Submitting download task for: {}", fileUrl); + // Call on the FX Thread if (Platform.isFxApplicationThread()) { Exception exception = new InvalidDownloadThreadException(); logger.error(exception.getMessage(), exception); return; } - performDownload(fileUrl, outputFile); + executorService.submit(() -> performDownload(fileUrl, outputFile, expectedHash, hashAlgorithm)); } - private void performDownload(String fileUrl, File outputFile) { + private void performDownload(String fileUrl, File outputFile, String expectedHash, String hashAlgorithm) { DownloadInfo info = null; try { URL url = new URI(fileUrl).toURL(); @@ -57,17 +71,23 @@ private void performDownload(String fileUrl, File outputFile) { connection.setConnectTimeout(5000); requestProperties.forEach(connection::setRequestProperty); - activeConnections.put(fileUrl, connection); // Store the connection + activeConnections.put(fileUrl, connection); long fileSize = connection.getContentLengthLong(); String fileName = url.getFile().substring(url.getFile().lastIndexOf('/') + 1); - // Initialize Info and Report Start info = new DownloadInfo(fileName, fileSize, fileUrl); - activeDownloads.put(fileUrl, info); // Track active download + activeDownloads.put(fileUrl, info); handleStart(info); - // + // Ensure the parent directories exist before writing + File parentDir = outputFile.getParentFile(); + if (parentDir != null && !parentDir.exists()) { + if (!parentDir.mkdirs()) { + throw new IOException("Failed to create parent directories for output file."); + } + } + try (InputStream inputStream = connection.getInputStream(); FileOutputStream outputStream = new FileOutputStream(outputFile)) { @@ -76,39 +96,47 @@ private void performDownload(String fileUrl, File outputFile) { long totalBytesDownloaded = 0; long lastNotificationTime = System.currentTimeMillis(); - // Loop the download bytes until completed. while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); totalBytesDownloaded += bytesRead; info.setDownloadedBytes(totalBytesDownloaded); - // Throttle progress updates if (System.currentTimeMillis() - lastNotificationTime > 200) { handleProgress(info); lastNotificationTime = System.currentTimeMillis(); } } - info.setComplete(true); - handleProgress(info); // Final progress update - handleComplete(info, outputFile); - } catch (IOException e) { String message = e.getMessage() != null ? e.getMessage().toLowerCase() : ""; - // Check for controlled cancellation errors (stream/socket closure) - // These messages signal that the stream was closed by cancelDownload(url) if (message.contains("socket closed") || message.contains("stream closed") || message.contains("operation canceled")) { - // This is a controlled stop. We don't treat it as a failure. - // The handleCancel event has already fired. logger.info("Download task for {} stopped by user or interruption.", info.getFileName()); + return; // Exit early since it was cancelled } else { - handleError(info, e); + throw e; // Rethrow to be caught by the outer catch block } } + if (expectedHash != null && !expectedHash.trim().isEmpty()) { + String algorithm = (hashAlgorithm != null && !hashAlgorithm.trim().isEmpty()) ? hashAlgorithm : "SHA-256"; + logger.info("Verifying file integrity using {}...", algorithm); + + boolean isHashValid = verifyFileHash(outputFile, expectedHash, algorithm); + if (!isHashValid) { + if (outputFile.exists() && !outputFile.delete()) { + logger.warn("Failed to delete tampered file: {}", outputFile.getAbsolutePath()); + } + throw new SecurityException("Hash mismatch! The file may be corrupted or maliciously altered."); + } + logger.info("File integrity verified successfully."); + } + + info.setComplete(true); + handleProgress(info); // Final progress update + handleComplete(info, outputFile); + } catch (Exception e) { - // URL/Connection setup error if (info == null) { info = new DownloadInfo("Unknown", -1, fileUrl); } @@ -121,30 +149,53 @@ private void performDownload(String fileUrl, File outputFile) { } } + /** + * Verifies the cryptographic hash of a downloaded file. + */ + private boolean verifyFileHash(File file, String expectedHash, String algorithm) throws Exception { + MessageDigest digest = MessageDigest.getInstance(algorithm); + + try (InputStream fis = new FileInputStream(file)) { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = fis.read(buffer)) != -1) { + digest.update(buffer, 0, bytesRead); + } + } + + byte[] hashBytes = digest.digest(); + StringBuilder hexString = new StringBuilder(); + + for (byte b : hashBytes) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + + return hexString.toString().equalsIgnoreCase(expectedHash); + } + public boolean cancelDownload(String fileUrl) { DownloadInfo info = activeDownloads.remove(fileUrl); URLConnection connection = activeConnections.remove(fileUrl); - // Force the stream/connection to close to unstick the I/O thread if (connection != null) { try { if (connection instanceof HttpURLConnection) { ((HttpURLConnection) connection).disconnect(); } } catch (Exception ignored) { - // Ignore any exception on forced close } } - // Interrupt the thread - boolean wasRunning = false; - - // Call the cancel listeners if (info != null) { handleCancel(info); + return true; } - return wasRunning; + return false; } public long getRemoteFileSize(String fileUrl) throws IOException { @@ -175,7 +226,7 @@ public void removeDownloadListener(DownloadListener listener) { private void handleStart(DownloadInfo info) { logger.info("Download Started: {}", info.getFileName()); - logger.info("Total Size: {}", (info.getTotalFileSize() > 0 ? info.getTotalFileSize() + "bytes" : "Unknown")); + logger.info("Total Size: {} bytes", (info.getTotalFileSize() > 0 ? info.getTotalFileSize() : "Unknown")); for (DownloadListener listener : listeners) { listener.onDownloadStart(info); } @@ -209,18 +260,10 @@ private void handleCancel(DownloadInfo info) { } } - /** - * Gathers and returns the download info for a specific file URL. - * @param fileUrl The unique identifier (URL) of the download task. - * @return The DownloadInfo object for that task, or null if it was never started. - */ public DownloadInfo getDownloadInfo(String fileUrl) { return activeDownloads.get(fileUrl); } - /** - * @return A map of all tracked downloads (active, completed, or failed). - */ public ConcurrentHashMap getAllDownloadStatuses() { return activeDownloads; } @@ -231,6 +274,12 @@ public ConcurrentHashMap getAllDownloadStatuses() { public void shutdown() { activeDownloads.clear(); listeners.clear(); + + // Shut down the thread pool to prevent application hang on exit + if (executorService != null && !executorService.isShutdown()) { + executorService.shutdownNow(); + } + logger.info("Downloader service shut down."); } } \ No newline at end of file diff --git a/src/main/java/me/piitex/os/GitHubUtil.java b/src/main/java/me/piitex/os/GitHubUtil.java index 24c839f..4568442 100644 --- a/src/main/java/me/piitex/os/GitHubUtil.java +++ b/src/main/java/me/piitex/os/GitHubUtil.java @@ -15,24 +15,13 @@ * Utility for GitHub REST API. Used for automatically updating and pulling information from releases and assets. */ public class GitHubUtil { - private final String repositoryUrl; //https://api.github.com/repos/owner/repo/ + private final String repositoryUrl; private static final Logger logger = LoggerFactory.getLogger(GitHubUtil.class); - /** - * Initializes the base url for the repository. To get the repository link, use the following format. - *

-     *     https://api.github.com/repos/$OWNER/$REPO/
-     * 
- * @param repositoryUrl The GitHub REST api repository link. - */ + public GitHubUtil(String repositoryUrl) { this.repositoryUrl = repositoryUrl; } - /** - * Gathers the latest release of the repository into a {@link JSONObject}. - * @return {@link JSONObject} of the latest release. - * @throws IOException If a connection cannot be made. - */ public JSONObject getLatestReleaseJson() throws IOException, URISyntaxException { URL url = new URI(repositoryUrl + "releases/latest").toURL(); logger.info("Fetching latest release request '{}'", url.toString()); @@ -40,9 +29,7 @@ public JSONObject getLatestReleaseJson() throws IOException, URISyntaxException connection.setConnectTimeout(1000); connection.setRequestMethod("GET"); - int responseCode = connection.getResponseCode(); - - if (responseCode == HttpURLConnection.HTTP_OK) { + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { String inputLine; StringBuilder response = new StringBuilder(); @@ -57,19 +44,16 @@ public JSONObject getLatestReleaseJson() throws IOException, URISyntaxException public int getLatestReleaseID() throws IOException, URISyntaxException { JSONObject object = getLatestReleaseJson(); - return object.getInt("id"); + return object != null ? object.getInt("id") : -1; } public JSONArray getReleaseAssets(int releaseID) throws IOException, URISyntaxException { URL url = new URI(repositoryUrl + "releases/" + releaseID + "/assets").toURL(); logger.info("Fetching release asset '{}' '{}'", releaseID, url.toString()); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - int responseCode = connection.getResponseCode(); - - if (responseCode == HttpURLConnection.HTTP_OK) { + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { String inputLine; StringBuilder response = new StringBuilder(); @@ -79,7 +63,7 @@ public JSONArray getReleaseAssets(int releaseID) throws IOException, URISyntaxEx return new JSONArray(response.toString()); } } else { - throw new RuntimeException("GitHub API request failed. Response Code: " + responseCode); + throw new RuntimeException("GitHub API request failed. Response Code: " + connection.getResponseCode()); } } @@ -94,11 +78,64 @@ public JSONObject getReleaseAsset(int releaseID, String pattern) throws IOExcept return null; } + /** + * Downloads an asset by ID. Extracts the hash directly from the asset's "digest" JSON field. + */ public FileDownloader downloadAsset(int assetId, File output, DownloadListener callback) { FileDownloader downloader = new FileDownloader(); - downloader.addDownloadListener(callback); + if (callback != null) { + downloader.addDownloadListener(callback); + } downloader.addRequestProperty("Accept", "application/octet-stream"); - downloader.startDownload(repositoryUrl + "releases/" + "assets/" + assetId, output); + + String expectedHash = null; + String hashAlgorithm = null; + + try { + URL assetUrl = new URI(repositoryUrl + "releases/assets/" + assetId).toURL(); + HttpURLConnection assetConn = (HttpURLConnection) assetUrl.openConnection(); + assetConn.setRequestMethod("GET"); + assetConn.setRequestProperty("Accept", "application/vnd.github.v3+json"); + + if (assetConn.getResponseCode() == HttpURLConnection.HTTP_OK) { + try (BufferedReader in = new BufferedReader(new InputStreamReader(assetConn.getInputStream()))) { + StringBuilder response = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) response.append(line); + + JSONObject assetJson = new JSONObject(response.toString()); + String digest = assetJson.optString("digest", ""); + + if (!digest.isEmpty() && digest.contains(":")) { + String[] parts = digest.split(":", 2); + hashAlgorithm = formatAlgorithm(parts[0]); + expectedHash = parts[1]; + } + } + } + } catch (Exception e) { + logger.warn("Failed to retrieve digest via GitHub API for asset ID {}. Proceeding without verification.", assetId, e); + } + + if (expectedHash != null && hashAlgorithm != null) { + logger.info("Found {} hash for asset {}: {}", hashAlgorithm, assetId, expectedHash); + downloader.startDownload(repositoryUrl + "releases/assets/" + assetId, output, expectedHash, hashAlgorithm); + } else { + logger.warn("No digest found for asset {}. Downloading without verification.", assetId); + downloader.startDownload(repositoryUrl + "releases/assets/" + assetId, output); + } + return downloader; } -} + + /** + * Maps GitHub's algorithm string to standard Java MessageDigest algorithm names. + */ + private String formatAlgorithm(String rawAlg) { + String upperAlg = rawAlg.toUpperCase(); + if (upperAlg.equals("SHA256")) return "SHA-256"; + if (upperAlg.equals("SHA512")) return "SHA-512"; + if (upperAlg.equals("SHA1")) return "SHA-1"; + return upperAlg; // Fallback to whatever was provided + } +} \ No newline at end of file From badef7dddbd3791bf0421c27b86a1a99f52383df Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 7 Apr 2026 23:21:56 -0500 Subject: [PATCH 64/78] Added exceptions when using an invalid addElement function. --- .../piitex/engine/containers/CardContainer.java | 8 ++++---- .../engine/containers/DialogueContainer.java | 9 ++++----- .../engine/containers/PaginationContainer.java | 17 ++++++++++++++++- .../piitex/engine/containers/TileContainer.java | 9 +++++---- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/main/java/me/piitex/engine/containers/CardContainer.java b/src/main/java/me/piitex/engine/containers/CardContainer.java index 42c1b6c..297f758 100644 --- a/src/main/java/me/piitex/engine/containers/CardContainer.java +++ b/src/main/java/me/piitex/engine/containers/CardContainer.java @@ -34,7 +34,7 @@ public CardContainer(double width, double height) { */ @Override public void addElements(Element... elements) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type!"); } /** @@ -42,7 +42,7 @@ public void addElements(Element... elements) { */ @Override public void addElements(LinkedList elements) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type!"); } /** @@ -50,7 +50,7 @@ public void addElements(LinkedList elements) { */ @Override public void addElement(Element element) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type!"); } /** @@ -58,7 +58,7 @@ public void addElement(Element element) { */ @Override public void addElement(Element element, int index) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type!"); } public void setHeader(Element element) { diff --git a/src/main/java/me/piitex/engine/containers/DialogueContainer.java b/src/main/java/me/piitex/engine/containers/DialogueContainer.java index 9bbc201..ebdb59c 100644 --- a/src/main/java/me/piitex/engine/containers/DialogueContainer.java +++ b/src/main/java/me/piitex/engine/containers/DialogueContainer.java @@ -73,7 +73,7 @@ public Pane getPane() { */ @Override public void addElement(Element element) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type! Use 'setHeader(), setBody()'"); } /** @@ -81,7 +81,7 @@ public void addElement(Element element) { */ @Override public void addElement(Element element, int index) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type! Use 'setHeader(), setBody()'"); } /** @@ -89,7 +89,7 @@ public void addElement(Element element, int index) { */ @Override public void addElements(Element... elements) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type! Use 'setHeader(), setBody()'"); } /** @@ -97,10 +97,9 @@ public void addElements(Element... elements) { */ @Override public void addElements(LinkedList elements) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type! Use 'setHeader(), setBody()'"); } - @Override public Node build() { pane.setTranslateX(getX()); diff --git a/src/main/java/me/piitex/engine/containers/PaginationContainer.java b/src/main/java/me/piitex/engine/containers/PaginationContainer.java index c0bf8de..383d780 100644 --- a/src/main/java/me/piitex/engine/containers/PaginationContainer.java +++ b/src/main/java/me/piitex/engine/containers/PaginationContainer.java @@ -73,6 +73,21 @@ public Node build() { */ @Override public void addElement(Element element) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type!"); + } + + @Override + public void addElement(Element element, int index) { + throw new UnsupportedOperationException("Method is not supported with this container type!"); + } + + @Override + public void addElements(Element... elements) { + throw new UnsupportedOperationException("Method is not supported with this container type!"); + } + + @Override + public void addElements(LinkedList elements) { + throw new UnsupportedOperationException("Method is not supported with this container type!"); } } \ No newline at end of file diff --git a/src/main/java/me/piitex/engine/containers/TileContainer.java b/src/main/java/me/piitex/engine/containers/TileContainer.java index b9be838..577827d 100644 --- a/src/main/java/me/piitex/engine/containers/TileContainer.java +++ b/src/main/java/me/piitex/engine/containers/TileContainer.java @@ -66,24 +66,25 @@ public Element getAction() { @Override public void addElements(Element... elements) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type!"); } @Override public void addElements(LinkedList elements) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type!"); } @Override public void addElement(Element element) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type!"); } @Override public void addElement(Element element, int index) { - return; + throw new UnsupportedOperationException("Method is not supported with this container type!"); } + public Tile getTile() { return tile; } From 456f45a4401993e896a9856165d84b5e78844fcf Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 7 Apr 2026 23:22:18 -0500 Subject: [PATCH 65/78] Added DownloadContainer for software updates. --- .../engine/containers/DownloadContainer.java | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 src/main/java/me/piitex/engine/containers/DownloadContainer.java diff --git a/src/main/java/me/piitex/engine/containers/DownloadContainer.java b/src/main/java/me/piitex/engine/containers/DownloadContainer.java new file mode 100644 index 0000000..2018c24 --- /dev/null +++ b/src/main/java/me/piitex/engine/containers/DownloadContainer.java @@ -0,0 +1,177 @@ +package me.piitex.engine.containers; + +import javafx.application.Platform; +import javafx.geometry.Pos; +import me.piitex.engine.layouts.VerticalLayout; +import me.piitex.engine.overlays.ProgressBarOverlay; +import me.piitex.engine.overlays.TextOverlay; +import me.piitex.os.DownloadInfo; +import me.piitex.os.DownloadListener; +import me.piitex.os.FileDownloader; + +import java.io.File; +import java.util.function.Consumer; + +public class DownloadContainer extends EmptyContainer { + private final String label; + private String url; + private File output; + private final FileDownloader downloader; + private DownloadInfo downloadInfo; + + private VerticalLayout main; + private TextOverlay message, downloadText; + private ProgressBarOverlay downloadProgress; + + private Consumer downloadComplete; + private Consumer downloadError; + private Consumer downloadCancelled; + + + public DownloadContainer(double width, double height, String label, String url, File output) { + super(0, 0, width, height); + this.label = label; + this.url = url; + this.output = output; + this.downloader = new FileDownloader(); + init(); + } + + public DownloadContainer(double width, double height, String label, DownloadInfo downloadInfo, FileDownloader downloader) { + super(0, 0, width, height); + this.label = label; + this.downloadInfo = downloadInfo; + this.downloader = downloader; + init(); + } + + public Consumer getOnDownloadComplete() { + return downloadComplete; + } + + public void onDownloadComplete(Consumer downloadComplete) { + this.downloadComplete = downloadComplete; + } + + public Consumer getDownloadError() { + return downloadError; + } + + public void onDownloadError(Consumer downloadError) { + this.downloadError = downloadError; + } + + public Consumer getDownloadCancelled() { + return downloadCancelled; + } + + public void onDownloadCancelled(Consumer downloadCancelled) { + this.downloadCancelled = downloadCancelled; + } + + private void init() { + main = new VerticalLayout(getWidth(), getHeight()); + main.setMaxSize(main.getWidth(), main.getHeight()); + main.setAlignment(Pos.TOP_CENTER); + main.setY(20); + addElement(main); + + message = new TextOverlay(label); + main.addElement(message); + + downloadProgress = new ProgressBarOverlay(); + main.addElement(downloadProgress); + + if (downloader.getListeners().isEmpty()) { + hookDownloadListeners(); + } + + downloadText = new TextOverlay("0/0"); + main.addElement(downloadText); + } + + public void startDownload() { + if (url != null && output != null) { + downloader.startDownload(url, output); + } else if (downloadInfo != null) { + downloader.startDownload(downloadInfo.getDownloadUrl(), downloadInfo.getOutput()); + } else { + throw new RuntimeException("Could not initialize download. URL or output not specified."); + } + } + + private void hookDownloadListeners() { + downloader.addDownloadListener(new DownloadListener() { + @Override + public void onDownloadStart(DownloadInfo info) { + Platform.runLater(() -> { + downloadProgress.getProgressBar().progressProperty().set(0); + }); + } + + @Override + public void onDownloadProgress(DownloadInfo info) { + Platform.runLater(() -> { + downloadProgress.getProgressBar().progressProperty().set(info.getDownloadProgress()); + + if (info.getTotalFileSize() < 900000) { // Use Kb + downloadText.setText(info.getDownloadedBytes() / 1024 + "/" + info.getTotalFileSize() / 1024 + "KiB"); + } else if (info.getTotalFileSize() < 900000000) { // Use MB + downloadText.setText(info.getDownloadedBytes() / 1000000 + "/" + info.getTotalFileSize() / 1000000 + "MB"); + } else { // Use GB + downloadText.setText(info.getDownloadedBytes() / 1000000000 + "/" + info.getTotalFileSize() / 1000000000 + "GB"); + } + + + }); + } + + @Override + public void onDownloadComplete(DownloadInfo info, File outputFile) { + if (downloadComplete != null) { + downloadComplete.accept(info); + } else { + throw new RuntimeException("Download completion is not handled!"); + } + } + + @Override + public void onDownloadError(DownloadInfo info, Exception e) { + if (downloadError != null) { + downloadError.accept(info); + } else { + throw new RuntimeException("Download error is not handled!"); + } + } + + @Override + public void onDownloadCancel(DownloadInfo info) { + if (downloadCancelled != null) { + downloadCancelled.accept(info); + } else { + throw new RuntimeException("Download cancelled is not handled!"); + } + } + }); + } + + public FileDownloader getDownloader() { + return downloader; + } + + public VerticalLayout getMain() { + return main; + } + + public TextOverlay getMessage() { + return message; + } + + public ProgressBarOverlay getDownloadProgress() { + return downloadProgress; + } + + public TextOverlay getDownloadText() { + return downloadText; + } +} From 3c2358f8aa7911a9b42294fb786ee9a61195b685 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 7 Apr 2026 23:22:38 -0500 Subject: [PATCH 66/78] Reverted ImageLoader constructors to support RenJava. --- .../me/piitex/engine/loaders/ImageLoader.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/piitex/engine/loaders/ImageLoader.java b/src/main/java/me/piitex/engine/loaders/ImageLoader.java index 67339e8..edc624a 100644 --- a/src/main/java/me/piitex/engine/loaders/ImageLoader.java +++ b/src/main/java/me/piitex/engine/loaders/ImageLoader.java @@ -26,8 +26,22 @@ public class ImageLoader { public static boolean useCache = true; - public ImageLoader(String path) { - this.file = new File(path); + /** + * Loads an image via a filename from the base directory. + * @param name Name of the image file. + */ + public ImageLoader(String name) { + File directory = new File(System.getProperty("user.dir") + "/game/images/"); + this.file = new File(directory, name); + } + + public ImageLoader(String directory, String name) { + File fileDirectory = new File(System.getProperty("user.dir") + "/" + directory + "/"); + this.file = new File(fileDirectory, name); + } + + public ImageLoader(File directory, String name) { + this.file = new File(directory, name); } public ImageLoader(File file) { From 1095ec96a01251de593f2cfac9033831af14c2be Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 7 Apr 2026 23:23:03 -0500 Subject: [PATCH 67/78] Added custom css styling to buttons. --- .../piitex/engine/overlays/ButtonOverlay.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java b/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java index 483a64d..470944d 100644 --- a/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java +++ b/src/main/java/me/piitex/engine/overlays/ButtonOverlay.java @@ -8,23 +8,25 @@ import javafx.scene.paint.Paint; import me.piitex.engine.Element; import me.piitex.engine.loaders.FontLoader; -import org.kordamp.ikonli.javafx.FontIcon; +import java.util.ArrayList; import java.util.LinkedList; +import java.util.List; public class ButtonOverlay extends Overlay implements Region { private final Button button; private final String id; private String text; private FontLoader font; - private Element graphic; + private final Element graphic; private final IconOverlay icon; private final LinkedList images = new LinkedList<>(); private Paint textFill; private Pos alignment; - private double width, height, prefHeight, prefWidth, maxWidth, maxHeight; + public static List cssFiles = new ArrayList<>(); + /** * Private constructor used by the {@link ButtonBuilder}. * @param builder The builder instance with all the properties set. @@ -120,6 +122,9 @@ public Node render() { Node graphic = icon.assemble(); button.setGraphic(graphic); } + if (graphic != null) { + button.setGraphic(graphic.assemble()); + } if (text != null && !text.isEmpty()) { button.setText(text); } @@ -129,9 +134,6 @@ public Node render() { if (textFill != null) { button.setTextFill(textFill); } - if (graphic != null) { - button.setGraphic(graphic.assemble()); - } if (alignment != null) { button.setAlignment(alignment); } @@ -152,6 +154,11 @@ public Node render() { button.setTranslateX(getX()); button.setTranslateY(getY()); + + for (String file : cssFiles) { + button.getStylesheets().add(file); + } + return button; } From 79e7c166e2fa879e9178731060a43d2a2dcf034f Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 7 Apr 2026 23:23:27 -0500 Subject: [PATCH 68/78] Added generic events to overlays. --- src/main/java/me/piitex/engine/overlays/Overlay.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/me/piitex/engine/overlays/Overlay.java b/src/main/java/me/piitex/engine/overlays/Overlay.java index 1372b64..1a39d36 100644 --- a/src/main/java/me/piitex/engine/overlays/Overlay.java +++ b/src/main/java/me/piitex/engine/overlays/Overlay.java @@ -59,10 +59,6 @@ public abstract class Overlay extends Element { private double x,y; private String tooltip; - private IOverlayHover iOverlayHover; - private IOverlayHoverExit iOverlayHoverExit; - private IOverlayClick iOverlayClick; - private IOverlayClickRelease iOverlayClickRelease; private IOverlaySubmit iOverlaySubmit; private Cursor cursor; From be314c40340dad5523159df25ff7c75a7270b50b Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 7 Apr 2026 23:23:54 -0500 Subject: [PATCH 69/78] Added generic events to elements. --- src/main/java/me/piitex/engine/Element.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/me/piitex/engine/Element.java b/src/main/java/me/piitex/engine/Element.java index dcca87c..469c070 100644 --- a/src/main/java/me/piitex/engine/Element.java +++ b/src/main/java/me/piitex/engine/Element.java @@ -144,6 +144,22 @@ public void onMouseExit(Consumer mouseExitConsumer) { } } + public Consumer getOnClick() { + return clickConsumer; + } + + public Consumer getOnRelease() { + return clickReleaseConsumer; + } + + public Consumer getOnMouseExit() { + return mouseExitConsumer; + } + + public Consumer getOnHover() { + return hoverConsumer; + } + /** * Assembles the element into its JavaFX {@link Node}. * From 6bd89c98b739745844218e5ce128e6523d248802 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 7 Apr 2026 23:24:12 -0500 Subject: [PATCH 70/78] Removed old clean function. --- src/main/java/me/piitex/engine/Window.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main/java/me/piitex/engine/Window.java b/src/main/java/me/piitex/engine/Window.java index 47957b7..3f7cf59 100644 --- a/src/main/java/me/piitex/engine/Window.java +++ b/src/main/java/me/piitex/engine/Window.java @@ -472,16 +472,6 @@ 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 all containers and resets the window's root pane and scene. * The stage is not automatically shown after this operation. From 2ed944e4e9a3a1107b130ecfe96bc361bda316b0 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 7 Apr 2026 23:24:36 -0500 Subject: [PATCH 71/78] Migrated from plain text to binary. --- .../me/piitex/os/configurations/InfoFile.java | 108 ++++++++---------- 1 file changed, 46 insertions(+), 62 deletions(-) diff --git a/src/main/java/me/piitex/os/configurations/InfoFile.java b/src/main/java/me/piitex/os/configurations/InfoFile.java index c1473c5..3afe4aa 100644 --- a/src/main/java/me/piitex/os/configurations/InfoFile.java +++ b/src/main/java/me/piitex/os/configurations/InfoFile.java @@ -32,7 +32,7 @@ public InfoFile() { * Constructs a new {@code InfoFile} instance and associates it with a physical file. *

* If the file does not exist, it will be created. If the file exists, its contents - * will be loaded into memory. The file's contents will be decrypted if + * will be loaded into memory via a binary stream. The file's contents will be decrypted if * encryption is enabled. * * @param file the file to read from and write to. @@ -48,41 +48,45 @@ public InfoFile(File file, boolean encrypt) { logger.warn("Unable to create file '{}'", file.getAbsolutePath()); } } catch (IOException e) { - logger.error("IO exception occurred while creating character info!", e); + logger.error("IO exception occurred while creating info file!", e); } } else { - // Load file - Scanner scanner = null; File output = null; try { output = Files.createTempFile(null, ".info").toFile(); + File targetFile = file; + if (encrypt && !dev) { - // Decrypt file try { FileCrypter.decryptFile(file, output); - scanner = new Scanner(new FileInputStream(output)); + targetFile = output; } catch (IllegalBlockSizeException | IOException ignored) { - scanner = new Scanner(new FileInputStream(file)); + // Fallback or ignore if decryption fails } - } else { - scanner = new Scanner(new FileInputStream(file)); } - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (line.split("=").length > 1) { - entryMap.put(line.split("=")[0], line.split("=")[1].replace("!@!", "\n")); + + // Safeguard: Only attempt to read if the file actually has data + if (targetFile.length() > 0) { + try (DataInputStream dis = new DataInputStream(new FileInputStream(targetFile))) { + int mapSize = dis.readInt(); + for (int i = 0; i < mapSize; i++) { + String key = dis.readUTF(); + String value = dis.readUTF(); + entryMap.put(key, value); + } + } catch (EOFException e) { + logger.warn("Reached EOF while reading info file '{}'", file.getName()); + } catch (UTFDataFormatException e) { + logger.error("Data format error in info file '{}'. The file may be an old text version. Please delete it.", file.getName(), e); } } - } catch (IOException e) { - logger.error("An occurred during the initial encryption process.", e); + logger.error("An error occurred during the initial read process.", e); } finally { - if (scanner != null) { - scanner.close(); - } - if (output != null && !output.delete()) { - logger.warn("Unable to delete unencrypted file during initialization '{}'", output.getAbsolutePath()); - forceDelete(output); + if (output != null && output.exists()) { + if (!output.delete()) { + forceDelete(output); + } } } } @@ -195,12 +199,10 @@ public Double getDoubleOrDefault(String key, Double defaultValue) { public List getList(String key) { String value = entryMap.get(key); List toReturn = new ArrayList<>(); - if (!value.contains("!@!")) { + if (value == null || !value.contains("!@!")) { return toReturn; } - toReturn.addAll(Arrays.asList(value.split("!@!"))); - return toReturn; } @@ -215,12 +217,10 @@ public List getList(String key) { public LinkedList getLinkedList(String key) { String value = entryMap.get(key); LinkedList toReturn = new LinkedList<>(); - if (!value.contains("!@!")) { + if (value == null || !value.contains("!@!")) { return toReturn; } - toReturn.addAll(Arrays.asList(value.split("!@!"))); - return toReturn; } @@ -234,21 +234,16 @@ public LinkedList getLinkedList(String key) { */ public Map getStringMap(String key) { Map toReturn = new HashMap<>(); - // !@!key@!@value!@!key@!@value2!@! - String value = entryMap.get(key); - if (!value.contains("!&'!")) { + if (value == null || !value.contains("!&'!")) { return toReturn; } - for (String s : value.split("!&'!")) { - // s = key@!@value String[] split = s.split("@!@"); if (split.length > 1) { toReturn.put(split[0], split[1]); } } - return toReturn; } @@ -262,21 +257,16 @@ public Map getStringMap(String key) { */ public TreeMap getSortedStringMap(String key) { TreeMap toReturn = new TreeMap<>(); - // !@!key@!@value!@!key@!@value2!@! - String value = entryMap.get(key); - if (!value.contains("!&'!")) { + if (value == null || !value.contains("!&'!")) { return toReturn; } - for (String s : value.split("!&'!")) { - // s = key@!@value String[] split = s.split("@!@"); if (split.length > 1) { toReturn.put(split[0], split[1]); } } - return toReturn; } @@ -293,13 +283,12 @@ public boolean hasKey(String key) { /** * Sets a key-value pair. *

- * Newline characters in the value will be replaced with the "!@!" delimiter. + * Using binary serialization natively supports newline characters, meaning they no longer need to be delimited. * * @param key the key to set. * @param value the string value to set. */ public void set(String key, String value) { - value = value.replace("\n", "!@!"); entryMap.put(key, value); update(); } @@ -380,7 +369,7 @@ public void set(String key, Map map) { * Writes the current entry map to the associated file. *

* This method is called automatically after every {@link #set(String, String)} operation. - * The file will be encrypted if encryption is enabled. + * The file will be written using a binary stream and encrypted if encryption is enabled. * * @throws RuntimeException if an {@link IOException} occurs during file writing. */ @@ -394,32 +383,29 @@ public void update() { File output = null; try { - FileWriter writer; output = Files.createTempFile(null, ".info").toFile(); - if (encrypt && !dev) { - writer = new FileWriter(output); - } else { - writer = new FileWriter(file); - } - entryMap.forEach((s, s2) -> { - s2 = s2.replace("\n", "!@!"); - try { - writer.write(s + "=" + s2 + "\n"); - } catch (IOException e) { - throw new RuntimeException(e); + File targetFile = (encrypt && !dev) ? output : file; + + try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(targetFile))) { + dos.writeInt(entryMap.size()); + for (Map.Entry entry : entryMap.entrySet()) { + dos.writeUTF(entry.getKey()); + + // Safeguard against null values breaking the binary stream + String value = entry.getValue() == null ? "" : entry.getValue(); + dos.writeUTF(value); } - }); - writer.close(); + } + if (encrypt && !dev) { - // Replace output into the file FileCrypter.encryptFile(output, file); } } catch (IOException e) { - logger.error("An error occurred during the encryption save process.", e); + logger.error("An error occurred during the save process.", e); } finally { if (output != null && output.exists()) { if (!output.delete()) { - logger.error("Unable to delete unencrypted file after writing. '{}'", file.getAbsolutePath()); + logger.error("Unable to delete temp file after writing. '{}'", output.getAbsolutePath()); forceDelete(output); } } @@ -436,10 +422,8 @@ public void setEntryMap(Map entryMap) { public static InfoFile copy(InfoFile input) { InfoFile infoFile = new InfoFile(); - input.getEntryMap().forEach((s, s2) -> infoFile.getEntryMap().put(s, s2)); infoFile.update(); - return infoFile; } -} +} \ No newline at end of file From ebe34f53063f3d3974177e63a87ab43013f9e681 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 7 Apr 2026 23:25:26 -0500 Subject: [PATCH 72/78] Downloads now support hash checking. --- src/main/java/me/piitex/os/DownloadInfo.java | 10 ++- .../java/me/piitex/os/FileDownloader.java | 19 +++++- src/main/java/me/piitex/os/GitHubUtil.java | 63 ++++++++++++++----- 3 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/main/java/me/piitex/os/DownloadInfo.java b/src/main/java/me/piitex/os/DownloadInfo.java index 68005b8..477516d 100644 --- a/src/main/java/me/piitex/os/DownloadInfo.java +++ b/src/main/java/me/piitex/os/DownloadInfo.java @@ -1,15 +1,19 @@ package me.piitex.os; +import java.io.File; + public class DownloadInfo { private final String fileName; + private final File output; private final long totalFileSize; // Total size in bytes private long downloadedBytes; private double downloadProgress; // Between 0.0 and 1.0 private final String downloadUrl; private boolean isComplete; - public DownloadInfo(String fileName, long totalFileSize, String downloadUrl) { + public DownloadInfo(String fileName, File output, long totalFileSize, String downloadUrl) { this.fileName = fileName; + this.output = output; this.totalFileSize = totalFileSize; this.downloadUrl = downloadUrl; this.downloadedBytes = 0; @@ -21,6 +25,10 @@ public String getFileName() { return fileName; } + public File getOutput() { + return output; + } + public long getTotalFileSize() { return totalFileSize; } diff --git a/src/main/java/me/piitex/os/FileDownloader.java b/src/main/java/me/piitex/os/FileDownloader.java index 14e7846..66d16c1 100644 --- a/src/main/java/me/piitex/os/FileDownloader.java +++ b/src/main/java/me/piitex/os/FileDownloader.java @@ -34,6 +34,16 @@ public FileDownloader() { this.executorService = Executors.newCachedThreadPool(); } + /** + * Initializes the FileDownloader and adds GitHub API support. + * @param githubUrl Set to true to use GitHub API streams. + */ + public FileDownloader(boolean githubUrl) { + // Creates a thread pool that reuses previously constructed threads when they are available + this.executorService = Executors.newCachedThreadPool(); + addRequestProperty("Accept", "application/octet-stream"); + } + /** * Starts the download task asynchronously without hash checking. * @param fileUrl The URL of the file to download. @@ -76,7 +86,7 @@ private void performDownload(String fileUrl, File outputFile, String expectedHas long fileSize = connection.getContentLengthLong(); String fileName = url.getFile().substring(url.getFile().lastIndexOf('/') + 1); - info = new DownloadInfo(fileName, fileSize, fileUrl); + info = new DownloadInfo(fileName, outputFile, fileSize, fileUrl); activeDownloads.put(fileUrl, info); handleStart(info); @@ -138,7 +148,7 @@ private void performDownload(String fileUrl, File outputFile, String expectedHas } catch (Exception e) { if (info == null) { - info = new DownloadInfo("Unknown", -1, fileUrl); + info = new DownloadInfo("Unknown", outputFile, -1, fileUrl); } handleError(info, e); } finally { @@ -260,6 +270,7 @@ private void handleCancel(DownloadInfo info) { } } + public DownloadInfo getDownloadInfo(String fileUrl) { return activeDownloads.get(fileUrl); } @@ -268,6 +279,10 @@ public ConcurrentHashMap getAllDownloadStatuses() { return activeDownloads; } + public Set getListeners() { + return listeners; + } + /** * Shuts down the executor service cleanly. Should be called when the application exits. */ diff --git a/src/main/java/me/piitex/os/GitHubUtil.java b/src/main/java/me/piitex/os/GitHubUtil.java index 4568442..dea594a 100644 --- a/src/main/java/me/piitex/os/GitHubUtil.java +++ b/src/main/java/me/piitex/os/GitHubUtil.java @@ -6,10 +6,7 @@ import org.slf4j.LoggerFactory; import java.io.*; -import java.net.HttpURLConnection; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; +import java.net.*; /** * Utility for GitHub REST API. Used for automatically updating and pulling information from releases and assets. @@ -78,6 +75,30 @@ public JSONObject getReleaseAsset(int releaseID, String pattern) throws IOExcept return null; } + public JSONObject getAsset(int assetId) throws IOException, URISyntaxException { + URL url = new URI(repositoryUrl + "releases/assets/" + assetId).toURL(); + logger.info("Fetching asset '{}' '{}'", assetId, url.toString()); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { + try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String inputLine; + StringBuilder response = new StringBuilder(); + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + return new JSONObject(response.toString()); + } + } else { + throw new RuntimeException("GitHub API request failed. Response Code: " + connection.getResponseCode()); + } + } + + public long getAssetFileSize(int assetId) throws URISyntaxException, IOException { + return getAsset(assetId).getLong("size"); + } + /** * Downloads an asset by ID. Extracts the hash directly from the asset's "digest" JSON field. */ @@ -106,7 +127,7 @@ public FileDownloader downloadAsset(int assetId, File output, DownloadListener c JSONObject assetJson = new JSONObject(response.toString()); String digest = assetJson.optString("digest", ""); - if (!digest.isEmpty() && digest.contains(":")) { + if (digest.contains(":")) { String[] parts = digest.split(":", 2); hashAlgorithm = formatAlgorithm(parts[0]); expectedHash = parts[1]; @@ -117,25 +138,37 @@ public FileDownloader downloadAsset(int assetId, File output, DownloadListener c logger.warn("Failed to retrieve digest via GitHub API for asset ID {}. Proceeding without verification.", assetId, e); } - if (expectedHash != null && hashAlgorithm != null) { - logger.info("Found {} hash for asset {}: {}", hashAlgorithm, assetId, expectedHash); - downloader.startDownload(repositoryUrl + "releases/assets/" + assetId, output, expectedHash, hashAlgorithm); - } else { - logger.warn("No digest found for asset {}. Downloading without verification.", assetId); - downloader.startDownload(repositoryUrl + "releases/assets/" + assetId, output); + // Only start download if a callback is present. + if (callback != null) { + if (expectedHash != null) { + logger.info("Found {} hash for asset {}: {}", hashAlgorithm, assetId, expectedHash); + downloader.startDownload(repositoryUrl + "releases/assets/" + assetId, output, expectedHash, hashAlgorithm); + } else { + logger.warn("No digest found for asset {}. Downloading without verification.", assetId); + downloader.startDownload(repositoryUrl + "releases/assets/" + assetId, output); + } } return downloader; } + /** + * Prepares a GitHub asset for download. + */ + public DownloadInfo downloadAsset(int assetId, File output) throws URISyntaxException, IOException { + return new DownloadInfo(output.getName(), output, getAssetFileSize(assetId), repositoryUrl + "releases/assets/" + assetId); + } + /** * Maps GitHub's algorithm string to standard Java MessageDigest algorithm names. */ private String formatAlgorithm(String rawAlg) { String upperAlg = rawAlg.toUpperCase(); - if (upperAlg.equals("SHA256")) return "SHA-256"; - if (upperAlg.equals("SHA512")) return "SHA-512"; - if (upperAlg.equals("SHA1")) return "SHA-1"; - return upperAlg; // Fallback to whatever was provided + return switch (upperAlg) { + case "SHA256" -> "SHA-256"; + case "SHA512" -> "SHA-512"; + case "SHA1" -> "SHA-1"; + default -> upperAlg; + }; } } \ No newline at end of file From df3264417652ffb2e7ea853271f4c9793fe5fcf6 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Tue, 7 Apr 2026 23:25:55 -0500 Subject: [PATCH 73/78] FontLoader no longer uses old RenJava API. --- .../me/piitex/engine/loaders/FontLoader.java | 38 +++---------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/src/main/java/me/piitex/engine/loaders/FontLoader.java b/src/main/java/me/piitex/engine/loaders/FontLoader.java index 2ea1d45..2e58764 100644 --- a/src/main/java/me/piitex/engine/loaders/FontLoader.java +++ b/src/main/java/me/piitex/engine/loaders/FontLoader.java @@ -3,6 +3,8 @@ import javafx.scene.text.Font; import javafx.scene.text.FontPosture; import javafx.scene.text.FontWeight; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; @@ -16,6 +18,8 @@ public class FontLoader { private FontWeight weight; private FontPosture posture; + private static final Logger logger = LoggerFactory.getLogger(FontLoader.class); + public FontLoader(FontLoader font, double size) { this.name = font.getName(); this.size = size; @@ -50,40 +54,10 @@ public FontLoader(Font font, FontWeight weight, FontPosture posture, double size this.font = Font.font(font.getFamily(), weight, posture, size); } - public FontLoader(String name, double size) { - this.name = name; - File directory = new File(System.getProperty("user.dir") + "/game/fonts/"); - File file = new File(directory, name); - this.size = size; - try { - this.font = Font.loadFont(new FileInputStream(file), size); - } catch (FileNotFoundException e) { - URL resource = FontLoader.class.getClassLoader().getResource(name); - if (resource != null) { - this.font = Font.loadFont(resource.toExternalForm(), size); - } - } - } - - public FontLoader(String name) { - File directory = new File(System.getProperty("user.dir") + "/game/fonts/"); - File file = new File(directory, name); - this.name = name; - try { - this.font = Font.loadFont(new FileInputStream(file), 24); - } catch (FileNotFoundException e) { - URL resource = FontLoader.class.getClassLoader().getResource(name); - if (resource != null) { - this.font = Font.loadFont(resource.toExternalForm(), size); - } - } - this.size = 24; - } - - public FontLoader(File file, FontPosture posture, FontWeight weight) { + public FontLoader(File file, double size) { this.name = file.getName(); try { - this.font = Font.loadFont(new FileInputStream(file), 24); + this.font = Font.loadFont(new FileInputStream(file), size); } catch (FileNotFoundException e) { URL resource = FontLoader.class.getClassLoader().getResource(name); if (resource != null) { From b0c6b34a3bd36d4358f6366dbb0f842e42b67977 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Fri, 10 Apr 2026 09:27:37 -0500 Subject: [PATCH 74/78] Removed old comments. --- src/main/java/me/piitex/os/FileDownloader.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/me/piitex/os/FileDownloader.java b/src/main/java/me/piitex/os/FileDownloader.java index 66d16c1..58f6513 100644 --- a/src/main/java/me/piitex/os/FileDownloader.java +++ b/src/main/java/me/piitex/os/FileDownloader.java @@ -30,7 +30,6 @@ public class FileDownloader { * Initializes the FileDownloader and its thread pool. */ public FileDownloader() { - // Creates a thread pool that reuses previously constructed threads when they are available this.executorService = Executors.newCachedThreadPool(); } @@ -39,7 +38,6 @@ public FileDownloader() { * @param githubUrl Set to true to use GitHub API streams. */ public FileDownloader(boolean githubUrl) { - // Creates a thread pool that reuses previously constructed threads when they are available this.executorService = Executors.newCachedThreadPool(); addRequestProperty("Accept", "application/octet-stream"); } From 95f122f9f6519f74b822265c4b5a6e892070cce7 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Sat, 11 Apr 2026 20:57:48 -0500 Subject: [PATCH 75/78] Removed old comments. --- src/main/java/me/piitex/engine/containers/CardContainer.java | 2 -- src/main/java/me/piitex/engine/containers/EmptyContainer.java | 1 - 2 files changed, 3 deletions(-) diff --git a/src/main/java/me/piitex/engine/containers/CardContainer.java b/src/main/java/me/piitex/engine/containers/CardContainer.java index 297f758..793d3d0 100644 --- a/src/main/java/me/piitex/engine/containers/CardContainer.java +++ b/src/main/java/me/piitex/engine/containers/CardContainer.java @@ -16,8 +16,6 @@ public class CardContainer extends Container { private Element footer; public CardContainer(double x, double y, double width, double height) { - // Java 25 Update: You can now call code before the super() method - // I think this is slightly better than confusing casting. Card card = new Card(); this.atlantafxCard = card; super(card, x, y, width, height); diff --git a/src/main/java/me/piitex/engine/containers/EmptyContainer.java b/src/main/java/me/piitex/engine/containers/EmptyContainer.java index 0c1acb4..017870f 100644 --- a/src/main/java/me/piitex/engine/containers/EmptyContainer.java +++ b/src/main/java/me/piitex/engine/containers/EmptyContainer.java @@ -7,7 +7,6 @@ import me.piitex.engine.layouts.Layout; import me.piitex.engine.overlays.Overlay; -// A default container which contains no special layout. Completely normal container. /** * The EmptyContainer is a default {@link Container} which houses {@link Overlay}s, {@link Layout}s, and sub-containers. * The container must be added to a {@link Window} to be rendered. From f987dfadfc598367cdde4d84e79aa46a27a3053e Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Sat, 11 Apr 2026 20:58:20 -0500 Subject: [PATCH 76/78] Added JavaDoc comments. --- src/main/java/me/piitex/engine/Element.java | 141 ++++++- src/main/java/me/piitex/engine/Renderer.java | 397 ++++++++++++++++-- src/main/java/me/piitex/engine/Window.java | 286 ++++++++----- .../java/me/piitex/engine/WindowBuilder.java | 112 +++-- .../piitex/engine/containers/Container.java | 104 ++++- .../me/piitex/engine/overlays/Overlay.java | 123 ++++-- 6 files changed, 945 insertions(+), 218 deletions(-) diff --git a/src/main/java/me/piitex/engine/Element.java b/src/main/java/me/piitex/engine/Element.java index 469c070..2019a27 100644 --- a/src/main/java/me/piitex/engine/Element.java +++ b/src/main/java/me/piitex/engine/Element.java @@ -1,6 +1,5 @@ package me.piitex.engine; - import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.input.MouseEvent; @@ -18,13 +17,19 @@ import java.util.function.Consumer; /** - * Represents a graphical element that can be rendered to the {@link Window} or a {@link Container}. + * Represents the foundational graphical element that can be rendered inside a {@link Renderer} + * (which includes {@link Container} and {@link Layout}). + *

+ * This abstract base class provides the core lifecycle, state management, and event handling for all + * renderable components within the GUI framework. It acts as a bridge between the engine's logical + * representations and the underlying JavaFX {@link Node}. + *

*

- * 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). + * Elements are heavily dependent on their rendering index (Z-order). A lower index dictates that the + * element is drawn earlier, placing it underneath elements with a higher index. *

* + * @see Renderer * @see Container * @see Overlay * @see Layout @@ -42,19 +47,21 @@ public abstract class Element { private static final Logger logger = LoggerFactory.getLogger(Element.class); /** - * Retrieves the rendering index of this element. + * Retrieves the rendering index (Z-order) 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. + * @return The rendering index. A lower value indicates the element is rendered earlier (closer to the background). + * An index of 0 indicates automatic assignment by the engine. The lowest manual layer should generally be 1. */ public int getIndex() { return index; } /** - * Sets the rendering index of this element. + * Sets the rendering index (Z-order) 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. + * Modifying this value allows you to control the depth of the element on the screen. Higher index values + * will render the element on top of those with lower indices. Set to 0 to let the engine automatically + * assign the index based on insertion order. *

* * @param index The new rendering index for the element. @@ -63,6 +70,16 @@ public void setIndex(int index) { this.index = index; } + /** + * Toggles the interactive state of the element. + *

+ * When disabled, the underlying JavaFX node will no longer process user inputs or events. + * If the underlying {@link Node} has not been assembled or set yet, this will log an error + * rather than crashing the application. + *

+ * + * @param enabled {@code true} to enable interactions, {@code false} to disable them. + */ public void setEnabled(boolean enabled) { this.enabled = enabled; if (getNode() != null) { @@ -73,22 +90,55 @@ public void setEnabled(boolean enabled) { } } + /** + * Checks if the element is currently enabled for user interaction. + * + * @return {@code true} if the element is enabled, {@code false} otherwise. + */ public boolean isEnabled() { return enabled; } + /** + * Retrieves the cached JavaFX {@link Node} associated with this element. + *

+ * This cache is typically populated when the engine invokes the {@link #assemble()} method. + * If the element has not been assembled yet, this method will return null. + *

+ * + * @return The cached JavaFX node if present, {@code null} otherwise. + */ + @Nullable public Node getNode() { return node; } + /** + * Manually assigns the underlying JavaFX {@link Node} for this element. + * + * @param node The JavaFX node to associate with this engine element. + */ public void setNode(Node node) { this.node = node; } + /** + * Retrieves the currently assigned mouse cursor for this element. + * + * @return The JavaFX {@link Cursor} applied to this element when hovered, or {@code null} if default. + */ public Cursor getCursor() { return cursor; } + /** + * Sets a specific mouse cursor to display when the user hovers over this element. + *

+ * This automatically applies the cursor to the underlying JavaFX {@link Node} if it is currently cached. + *

+ * + * @param cursor The JavaFX {@link Cursor} to display. + */ public void setCursor(Cursor cursor) { this.cursor = cursor; if (node != null) { @@ -96,6 +146,14 @@ public void setCursor(Cursor cursor) { } } + /** + * Registers a callback to be fired when the user clicks on this element. + *

+ * This wraps the native JavaFX {@link MouseEvent#MOUSE_CLICKED} event into a framework-specific {@link ElementClickEvent}. + *

+ * + * @param clickConsumer The consumer to accept the click event logic. + */ public void onClick(Consumer clickConsumer) { this.clickConsumer = clickConsumer; @@ -108,6 +166,14 @@ public void onClick(Consumer clickConsumer) { } } + /** + * Registers a callback to be fired when the user releases a mouse click on this element. + *

+ * This wraps the native JavaFX {@link MouseEvent#MOUSE_RELEASED} event into a framework-specific {@link ElementClickReleaseEvent}. + *

+ * + * @param clickReleaseConsumer The consumer to accept the click release event logic. + */ public void onClickRelease(Consumer clickReleaseConsumer) { this.clickReleaseConsumer = clickReleaseConsumer; @@ -120,6 +186,14 @@ public void onClickRelease(Consumer clickReleaseConsum } } + /** + * Registers a callback to be fired when the user's mouse pointer enters the bounds of this element. + *

+ * This wraps the native JavaFX {@link MouseEvent#MOUSE_ENTERED} event into a framework-specific {@link ElementHoverEvent}. + *

+ * + * @param hoverConsumer The consumer to accept the hover event logic. + */ public void onHover(Consumer hoverConsumer) { this.hoverConsumer = hoverConsumer; @@ -132,6 +206,14 @@ public void onHover(Consumer hoverConsumer) { } } + /** + * Registers a callback to be fired when the user's mouse pointer exits the bounds of this element. + *

+ * This wraps the native JavaFX {@link MouseEvent#MOUSE_EXITED} event into a framework-specific {@link ElementExitEvent}. + *

+ * + * @param mouseExitConsumer The consumer to accept the mouse exit event logic. + */ public void onMouseExit(Consumer mouseExitConsumer) { this.mouseExitConsumer = mouseExitConsumer; @@ -144,36 +226,51 @@ public void onMouseExit(Consumer mouseExitConsumer) { } } + /** + * Retrieves the registered click event consumer. + * @return The consumer handling click events, or {@code null} if none is set. + */ public Consumer getOnClick() { return clickConsumer; } + /** + * Retrieves the registered click release event consumer. + * @return The consumer handling click release events, or {@code null} if none is set. + */ public Consumer getOnRelease() { return clickReleaseConsumer; } + /** + * Retrieves the registered mouse exit event consumer. + * @return The consumer handling mouse exit events, or {@code null} if none is set. + */ public Consumer getOnMouseExit() { return mouseExitConsumer; } + /** + * Retrieves the registered mouse hover event consumer. + * @return The consumer handling mouse hover events, or {@code null} if none is set. + */ public Consumer getOnHover() { return hoverConsumer; } /** - * 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()} - *

+ * Compiles and constructs the engine element into a standard JavaFX {@link Node}. *

- * For {@link Layout}'s it will return the {@link Layout#render()} result. + * This method is called by the engine during the rendering phase to translate custom elements + * into the native scene graph. *

- *

- * For {@link Container}'s it will return the {@link Container#build()} result. - *

- * @return The constructed node. + *
    + *
  • For {@link Overlay} instances, this invokes their specific rendering functions (e.g., {@link TextOverlay#render()}).
  • + *
  • For {@link Layout} instances, this invokes {@link Layout#render()}.
  • + *
  • For {@link Container} instances, this invokes {@link Container#build()}.
  • + *
+ * + * @return The fully constructed JavaFX node ready for the scene graph. */ public abstract Node assemble(); - -} +} \ No newline at end of file diff --git a/src/main/java/me/piitex/engine/Renderer.java b/src/main/java/me/piitex/engine/Renderer.java index e2cb3a2..79423e5 100644 --- a/src/main/java/me/piitex/engine/Renderer.java +++ b/src/main/java/me/piitex/engine/Renderer.java @@ -13,10 +13,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.util.*; /** - * An element which handles rendering of {@link Container}, {@link Layout}, and {@link Overlay} + * An element which handles the core visual rendering and structural management of child {@link Element}s. + *

+ * This class acts as the bridge between the engine's logical element hierarchy and the native + * JavaFX scene graph. It manages dimensions, styling, and nested elements via a z-index mapping. + * Subclasses like {@link Container} and {@link Layout} inherit this capability to manage + * their own distinct collections of children. + *

* * @see Layout * @see Container @@ -39,18 +46,39 @@ public class Renderer extends Element { // Handlers for events private IRendererKey iRendererKey; + /** + * Retrieves the custom key press event handler bound to this renderer. + * + * @return The active {@link IRendererKey} implementation, or null if none is set. + */ public IRendererKey getiRendererKey() { return iRendererKey; } + /** + * Binds a custom key press event handler to this renderer's underlying JavaFX node. + * The event will be attached during the {@link #assemble()} phase. + * + * @param iRendererKey The handler logic to execute on key press events. + */ public void onKeyPress(IRendererKey iRendererKey) { this.iRendererKey = iRendererKey; } + /** + * Retrieves the currently configured minimum width of the renderer. + * + * @return The width constraint in pixels. + */ public double getWidth() { return width; } + /** + * Sets the minimum width of the renderer and directly applies it to the underlying JavaFX Region. + * + * @param width The targeted width in pixels. + */ public void setWidth(double width) { this.width = width; if (getNode() instanceof Region region) { @@ -58,10 +86,20 @@ public void setWidth(double width) { } } + /** + * Retrieves the currently configured minimum height of the renderer. + * + * @return The height constraint in pixels. + */ public double getHeight() { return height; } + /** + * Sets the minimum height of the renderer and directly applies it to the underlying JavaFX Region. + * + * @param height The targeted height in pixels. + */ public void setHeight(double height) { this.height = height; @@ -70,14 +108,29 @@ public void setHeight(double height) { } } + /** + * Retrieves the preferred layout width of the renderer. + * + * @return The preferred width in pixels. + */ public double getPrefWidth() { return prefWidth; } + /** + * Retrieves the preferred layout height of the renderer. + * + * @return The preferred height in pixels. + */ public double getPrefHeight() { return prefHeight; } + /** + * Sets the preferred layout width of the renderer. Used by layout managers to compute sizes. + * + * @param prefWidth The targeted preferred width. + */ public void setPrefWidth(double prefWidth) { this.prefWidth = prefWidth; @@ -86,6 +139,11 @@ public void setPrefWidth(double prefWidth) { } } + /** + * Sets the preferred layout height of the renderer. Used by layout managers to compute sizes. + * + * @param prefHeight The targeted preferred height. + */ public void setPrefHeight(double prefHeight) { this.prefHeight = prefHeight; @@ -94,6 +152,12 @@ public void setPrefHeight(double prefHeight) { } } + /** + * Sets both the preferred layout width and height of the renderer simultaneously. + * + * @param width The targeted preferred width. + * @param height The targeted preferred height. + */ public void setPrefSize(double width, double height) { this.prefWidth = width; this.prefHeight = height; @@ -103,14 +167,30 @@ public void setPrefSize(double width, double height) { } } + /** + * Retrieves the maximum allowed width of the renderer constraint. + * + * @return The max width in pixels. + */ public double getMaxWidth() { return maxWidth; } + /** + * Retrieves the maximum allowed height of the renderer constraint. + * + * @return The max height in pixels. + */ public double getMaxHeight() { return maxHeight; } + /** + * Sets the absolute maximum dimensions this renderer is allowed to scale to. + * + * @param width The maximum width ceiling. + * @param height The maximum height ceiling. + */ public void setMaxSize(double width, double height) { this.maxWidth = width; this.maxHeight = height; @@ -120,72 +200,171 @@ public void setMaxSize(double width, double height) { } } + /** + * Retrieves the assigned background color logic. + * + * @return The JavaFX {@link Color} assigned as the background. + */ public Color getBackgroundColor() { return backgroundColor; } + /** + * Sets the background color. To visually update the node, {@link #setStyling(Node)} must be invoked + * during or after assembly. + * + * @param backgroundColor The JavaFX {@link Color} to apply. + */ public void setBackgroundColor(Color backgroundColor) { this.backgroundColor = backgroundColor; } + /** + * Retrieves the assigned border color logic. + * + * @return The JavaFX {@link Color} assigned as the border stroke. + */ public Color getBorderColor() { return borderColor; } + /** + * Sets the border color. To visually update the node, {@link #setStyling(Node)} must be invoked + * during or after assembly. + * + * @param borderColor The JavaFX {@link Color} to apply. + */ public void setBorderColor(Color borderColor) { this.borderColor = borderColor; } + /** + * Retrieves the stroke width of the renderer's border constraint. + * + * @return The border thickness. + */ public double getBorderWidth() { return borderWidth; } + /** + * Sets the uniform stroke thickness for the element's border. + * + * @param borderWidth The desired border thickness in pixels. + */ public void setBorderWidth(double borderWidth) { this.borderWidth = borderWidth; } + /** + * Retrieves the list of native JavaFX CSS style classes assigned to this renderer. + * + * @return A list of CSS class names. + */ public List getStyles() { return styles; } + /** + * Appends a new JavaFX CSS class to the renderer and binds it to the current native node immediately. + * + * @param style The CSS class string representation. + */ public void addStyle(String style) { styles.add(style); getNode().getStyleClass().add(style); } + /** + * Retrieves the top-level application Window context hosting this renderer. + * + * @return The parent {@link Window}. + */ public Window getWindow() { return window; } + /** + * Binds this renderer to a specific top-level application Window context. + * + * @param window The {@link Window} instance to associate with. + */ public void setWindow(Window window) { this.window = window; } - + /** + * Retrieves the mapped hierarchy of all child elements tracked by this renderer. + * Sorted inherently by their assigned index layout order. + * + * @return A TreeMap mapping integer indices to {@link Element} objects. + */ 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} + * Retrieves the child element structurally positioned at a specific z-index. + * + * @param index The positional index to search for. + * @return The matching {@link Element}, or null if no element occupies that index. */ + @Nullable public Element getElementAt(int index) { return elements.get(index); } + /** + * Retrieves the child element situated at the lowest assigned index constraint (bottom layer). + * + * @return The lowest indexed {@link Element}. + * @throws NoSuchElementException if the element map is empty. + */ public Element getFirstElement() { return elements.firstEntry().getValue(); } + /** + * Retrieves the child element situated at the highest assigned index constraint (top layer). + * + * @return The highest indexed {@link Element}. + * @throws NoSuchElementException if the element map is empty. + */ public Element getLastElement() { return elements.lastEntry().getValue(); } /** - * 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. + * Calculates the next available logical index (or uses its configured intrinsic index), + * maps the element, and immediately compiles it for the screen. + *

+ * Threading Constraint: This method immediately manipulates the live JavaFX scene graph + * and must be executed on the JavaFX Application Thread. If you are dispatching this + * from a background worker thread, you must handle the synchronization manually + * (e.g., via {@link javafx.application.Platform#runLater(Runnable)}). + *

+ *
+     * {@code
+     *
+     * // In application thread
+     * Renderer view = ... // Obtain the renderer
+     * Element element = ... // Initialize the element
+     * view.addElement(element);
+     *
+     * // In background thread
+     * new Thread(() -> {
+     *     Renderer view = ... // Obtain the renderer
+     *     Element element = ... // Initialize the element
+     *
+     *     // Synchronize the execution
+     *     Platform.runLater(() -> {
+     *         view.addElement(element);
+     *     });
+     * })
+     * }
+     * 
+ * + * @param element The {@link Element} component to append and render. */ public void addElement(Element element) { int index = element.getIndex(); @@ -197,10 +376,25 @@ public void addElement(Element element) { } /** - * Adds the element to the specific index. If there is an element already bound to that index it is shuffled forward. + * Injects an element into a specific z-index layout slot and forcefully renders it to the screen. + *

+ * Threading Constraint: This is a mutating operation that recursively shifts existing + * elements and immediately pushes the newly compiled {@link Node} into the live view layer. + * To prevent an {@link IllegalStateException}, this must be invoked on the JavaFX + * Application Thread. You can find more info in the base {@link #addElement(Element)} function. + *

+ *
+     * {@code
+     * Renderer view = ...
+     * Element element = ...
+     *
+     * // Inserts the element exactly at layer 5 and renders immediately
+     * view.addElement(element, 5);
+     * }
+     * 
* - * @param element The {@link Element} to add to the container. - * @param index The index/order of the element. + * @param element The {@link Element} to push and render. + * @param index The mandatory z-index layout order integer. */ public void addElement(Element element, int index) { Element current = elements.get(index); @@ -213,19 +407,29 @@ public void addElement(Element element, int index) { elements.put(index, element); Node node = element.assemble(); - if (element instanceof Overlay overlay) { - overlay.setNode(node); - } - if (element instanceof Renderer renderer) { - renderer.setNode(node); - } + element.setNode(node); + 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. + * Batch processes an array of elements, compiling and rendering each one sequentially. + *

+ * Threading Constraint: Because this delegates to {@link #addElement(Element)}, + * all view manipulations occur instantly. This method must be executed on the + * JavaFX Application Thread. You can find more info in the base {@link #addElement(Element)} function. + *

+ *
+     * {@code
+     * Element e1 = ...
+     * Element e2 = ...
+     *
+     * // Renders both elements sequentially
+     * view.addElements(e1, e2);
+     * }
+     * 
+ * + * @param elements The arbitrary array of {@link Element} components to append and render. */ public void addElements(Element... elements) { for (Element element : elements) { @@ -234,8 +438,24 @@ public void addElements(Element... elements) { } /** - * 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. + * Batch processes a linked list of elements, compiling and rendering each one sequentially. + *

+ * Threading Constraint: Because this delegates to {@link #addElement(Element)}, + * all view manipulations occur instantly. This method must be executed on the + * JavaFX Application Thread. You can find more info in the base {@link #addElement(Element)} function. + *

+ *
+     * {@code
+     * LinkedList list = new LinkedList<>();
+     * list.add(e1);
+     * list.add(e2);
+     *
+     * // Renders the entire list sequentially
+     * view.addElements(list);
+     * }
+     * 
+ * + * @param elements The {@link LinkedList} of child elements to add and render. */ public void addElements(LinkedList elements) { for (Element element : elements) { @@ -243,10 +463,22 @@ public void addElements(LinkedList elements) { } } + /** + * Identifies an element strictly by its stored z-index layout mapped key and entirely removes it + * from both the structural map and the visual JavaFX node representation. + * + * @param index The positional mapping key to eliminate. + */ public void removeElement(int index) { removeElement(getElementAt(index)); } + /** + * Explicitly unlinks a registered element from the structural index map and purges it + * from the active JavaFX rendering tree. + * + * @param element The targeted {@link Element} to delete. + */ public void removeElement(Element element) { elements.remove(element.getIndex()); if (element instanceof Overlay overlay) { @@ -257,14 +489,26 @@ public void removeElement(Element element) { } } + /** + * Retrieves and purges the lowest-indexed (bottommost) element structure. + */ public void removeFirstElement() { removeElement(elements.firstKey()); } + /** + * Retrieves and purges the highest-indexed (topmost) element structure. + */ public void removeLastElement() { removeElement(elements.lastKey()); } + /** + * Iterates explicitly through the index map to eliminate every structural reference + * tying matching a specific element instance. Safely handles bulk cleanup. + * + * @param element The targeted {@link Element} reference to scrub out completely. + */ public void removeAllElement(Element element) { LinkedHashMap toRemove = new LinkedHashMap<>(elements); toRemove.forEach((integer, e) -> { @@ -274,6 +518,14 @@ public void removeAllElement(Element element) { }); } + /** + * Relocates an already mapped element context from an existing indexed order + * cleanly into a new indexed target position. + * (Note: This mutates map logic but requires a re-render phase to push JavaFX Node hierarchy changes). + * + * @param oldIndex The index the element is currently sitting at. + * @param newIndex The fresh designated mapping layout destination. + */ public void moveElement(int oldIndex, int newIndex) { Element element = elements.get(oldIndex); if (element != null) { @@ -282,6 +534,10 @@ public void moveElement(int oldIndex, int newIndex) { } } + /** + * Performs a hard reset on the container system. Wipes all logical mappings + * alongside actively stripping the children from the parent JavaFX Pane. + */ public void removeAllElements() { elements.clear(); if (getNode() instanceof Pane pane) { @@ -289,6 +545,13 @@ public void removeAllElements() { } } + /** + * Replaces an element located at a specific index mapping. Overwrites both the logic map + * and strictly forces the underlying JavaFX hierarchy to compile and exchange view layers. + * + * @param index The location index to intercept. + * @param element The new replacement {@link Element}. + */ public void replaceElement(int index, Element element) { if (getNode() instanceof Pane pane) { pane.getChildren().remove(index); @@ -297,14 +560,33 @@ public void replaceElement(int index, Element element) { } } + /** + * Checks if an element resides structurally at the queried index coordinate. + * + * @param index The integer lookup key. + * @return True if populated; false otherwise. + */ public boolean containsElement(int index) { return elements.containsKey(index); } + /** + * Verifies if a specific element reference is currently being tracked visually in the map. + * + * @param element The targeted element logic constraint. + * @return True if the reference exists internally; false otherwise. + */ public boolean containsElement(Element element) { return elements.containsValue(element); } + /** + * The internal translation mechanism to bridge a compiled JavaFX Node straight into an active Pane. + * Respects safe fallback logic if an index allocation fails or extends out of bounds, preventing index crashes. + * + * @param node The fully compiled JavaFX visual component. + * @param index The expected hierarchy rendering order priority. + */ public void addToView(Node node, int index) { if (getNode() instanceof Pane pane) { if (!pane.getChildren().contains(node)) { @@ -335,28 +617,60 @@ public void addToView(Node node, int index) { } } + /** + * Strictly isolates the requested JavaFX Node structure and attempts to remove it + * exclusively out of the target renderer's underlying visual scope layer. + * + * @param node The raw target element. + */ public void removeFromView(Node node) { if (getNode() instanceof Pane pane) { pane.getChildren().remove(node); } } + /** + * Retrieves the custom lateral x-coordinate translation displacement configured for rendering. + * + * @return The offset distance. + */ public double getOffsetX() { return xOffset; } + /** + * Injects a lateral translation layout buffer padding along the horizontal x-axis. + * + * @param xOffset The explicit translation distance modification. + */ public void setOffsetX(double xOffset) { this.xOffset = xOffset; } + /** + * Retrieves the custom vertical y-coordinate translation displacement configured for rendering. + * + * @return The offset distance. + */ public double getOffsetY() { return yOffset; } + /** + * Injects a vertical translation layout buffer padding along the vertical y-axis. + * + * @param yOffset The explicit translation distance modification. + */ public void setOffsetY(double yOffset) { this.yOffset = yOffset; } + /** + * Interprets internal renderer CSS lists, background fills, and border properties + * compiling them out into inline raw CSS injections forcibly applied down upon the native JavaFX context node. + * + * @param node The physical target JavaFX node to mutate. + */ public void setStyling(Node node) { node.getStyleClass().addAll(styles); if (node instanceof Region region) { @@ -375,6 +689,11 @@ public void setStyling(Node node) { } } + /** + * Internal geometry translation handler enforcing horizontal and vertical custom offset constraints. + * + * @param node The rendered view. + */ private void updateOffsets(Node node) { if (getOffsetX() > 0 || getOffsetY() > 0) { node.setTranslateX(node.getTranslateX() + getOffsetX()); @@ -382,7 +701,13 @@ private void updateOffsets(Node node) { } } - // Helper css function + /** + * Converts a standard JavaFX Color object payload explicitly down into string-represented + * raw RGBA formatting syntax used internally by CSS parsers. + * + * @param color The incoming JavaFX color space requirement. + * @return A formatted `-fx-` compatible RGBA format string block. + */ private String cssColor(Color color) { return String.format("rgba(%d, %d, %d, %f)", (int) (255 * color.getRed()), @@ -391,7 +716,13 @@ private String cssColor(Color color) { color.getOpacity()); } - + /** + * The pivotal engine processing cycle. This iterates over the underlying logical structures + * (Layouts and Containers), handles structural translation, resolves nested hierarchy lists dynamically, + * builds and integrates keyboard logic routing, and ultimately fires local render listener chains cleanly. + * + * @return The freshly constructed and compiled primary structural JavaFX node. + */ @Override public Node assemble() { Node node = null; @@ -434,15 +765,33 @@ public Node assemble() { return node; } + /** + * Binds custom metadata properties natively onto the element footprint scope tracking memory. + * + * @param key The mapping string definition identifier. + * @param data The payload definition constraint. + */ public void addProperties(String key, String data) { properties.setProperty(key, data); } + /** + * Extracts a locally bound string-formatted data value linked by key. + * + * @param key The mapped lookup token to hunt down. + * @return The string data payload retrieved, or null if unassigned. + */ public String getProperty(String key) { return properties.getProperty(key); } + /** + * Verifies if specific logic or metadata string flags have been configured physically locally onto this exact element instance block scope. + * + * @param key The defined identification key string constraint. + * @return True if defined contextually within the engine mapping map scope; false otherwise. + */ public boolean hasProperty(String key) { return properties.containsKey(key); } -} +} \ No newline at end of file diff --git a/src/main/java/me/piitex/engine/Window.java b/src/main/java/me/piitex/engine/Window.java index 3f7cf59..3142670 100644 --- a/src/main/java/me/piitex/engine/Window.java +++ b/src/main/java/me/piitex/engine/Window.java @@ -31,7 +31,8 @@ /** * 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}. + * Instead of directly tracking arbitrary elements, the Window strictly houses and manages + * top-level {@link Container} objects. * *

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 @@ -47,8 +48,8 @@ * } * *

- * 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.

+ * To display UI elements within a window, a {@link Container} must first be created and populated. + * Then, that container is added directly to the window's managed collection.

*
{@code
  * Window window = application.getWindow();
  * Container container = new EmptyContainer(x, y, width, height);
@@ -57,17 +58,17 @@
  *
  * 

* All GUI-related functions, especially those involving scene graph modifications, - * must be executed on the JavaFX Application Thread.

+ * must be executed on the JavaFX Application Thread.

This example uses a native {@link Thread} for simplicity. *
{@code
- *     new Thread( () -> {
- *         // Code to be ran asynchronously
- *         loadBackend();
+ * new Thread( () -> {
+ * // Code to be executed asynchronously
+ * loadBackend();
  *
- *         Platform.runLater( () -> {
- *             // Any gui related code.
- *             initializeProgressIndicator();
- *         })
- *     })
+ * Platform.runLater( () -> {
+ * // Any gui related code.
+ * initializeProgressIndicator();
+ * })
+ * });
  * }
* * @see Container @@ -97,13 +98,18 @@ public class Window { private static final Logger logger = LoggerFactory.getLogger(Window.class); + // -------- OS Specific Values ---------- + // JavaFX does not take into account the window title bar height. + // This causes alignment issues with various operating systems. + private static final int LINUX_WINDOW_HEIGHT = 35; + private static final int WIN_WINDOW_HEIGHT = 40; + private static final int MAC_WINDOW_HEIGHT = 40; + + /** * 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()
@@ -140,7 +146,9 @@ public Window(WindowBuilder builder) {
 
     /**
      * Initializes the JavaFX Stage with the configured properties from the `WindowBuilder`.
-     * This method sets up the title, style, dimensions, icon, and initial scene.
+     * This method applies the title, style, dimensions, icon, scaling behaviors,
+     * anti-aliasing preferences, and initializes the root scene. Handles OS-specific
+     * window height discrepancies internally.
      */
     protected void buildStage() {
         stage = new Stage();
@@ -157,18 +165,18 @@ protected void buildStage() {
 
         // Linux, Windows, and Mac handle sizing of windows differently.
         // With the top control bar enabled, the height will be off.
-        // Offset the height by subtracting the height of the top bar.
-        if (OSUtil.getOS().contains("Windows")) {
-            stage.setHeight(height + 40);
-        } else if (OSUtil.getOS().contains("Linux")) {
-            stage.setHeight(height + 35);
+
+        if (OSUtil.getOS().toLowerCase().contains("linux")) {
+            stage.setHeight(height + LINUX_WINDOW_HEIGHT);
+        } else if (OSUtil.getOS().toLowerCase().contains("window")) {
+            stage.setHeight(height + WIN_WINDOW_HEIGHT);
+        } else if (OSUtil.getOS().toLowerCase().contains("mac")) {
+            stage.setHeight(height + MAC_WINDOW_HEIGHT);
         } else {
             stage.setHeight(height);
         }
-
         stage.setMaximized(maximized);
         stage.setFullScreen(fullscreen);
-
         root.setPrefSize(width, height);
 
         if (scale) {
@@ -193,7 +201,7 @@ protected void buildStage() {
 
     /**
      * Updates the background color of the window's root pane and scene.
-     * @param color The new background color.
+     * @param color The new background color to apply.
      */
     public void updateBackground(Color color) {
         this.backgroundColor = color;
@@ -224,6 +232,7 @@ public Stage getStage() {
     public Scene getScene() {
         return scene;
     }
+
     /**
      * Retrieves the root Pane of the window's scene graph.
      * @return The root Pane.
@@ -233,13 +242,18 @@ public Pane getRoot() {
     }
 
     /**
-     * Retrieves the configured width of the window.
+     * Retrieves the currently configured width of the window.
      * @return The window width.
      */
     public double getWidth() {
         return width;
     }
 
+    /**
+     * Sets a new width for the window. Updates the initial width tracker and clears any existing
+     * scaling transformations from the root pane.
+     * @param width The new width in pixels.
+     */
     public void setWidth(double width) {
         this.width = width;
         this.initialWidth = width;
@@ -247,6 +261,11 @@ public void setWidth(double width) {
         root.getTransforms().clear();
     }
 
+    /**
+     * Sets a new height for the window. Updates the initial height tracker and clears any existing
+     * scaling transformations from the root pane.
+     * @param height The new height in pixels.
+     */
     public void setHeight(double height) {
         this.height = height;
         this.initialHeight = height;
@@ -255,23 +274,34 @@ public void setHeight(double height) {
     }
 
     /**
-     * Retrieves the configured height of the window.
+     * Retrieves the currently configured height of the window.
      * @return The window height.
      */
     public double getHeight() {
         return height;
     }
 
+    /**
+     * Calculates the horizontal scale factor by dividing the current width by the initial width.
+     * Useful for dynamic resizing and responsive UI adjustments.
+     * @return The horizontal scaling multiplier.
+     */
     public double getWidthScale() {
         return width / initialWidth;
     }
 
+    /**
+     * Calculates the vertical scale factor by dividing the current height by the initial height.
+     * Useful for dynamic resizing and responsive UI adjustments.
+     * @return The vertical scaling multiplier.
+     */
     public double getHeightScale() {
         return height / initialHeight;
     }
 
     /**
      * Toggles the window between full-screen and windowed modes.
+     * Reverts to the explicitly set width and height if exiting full-screen mode.
      * @param fullscreen True to set to full-screen, false for windowed.
      */
     public void setFullscreen(boolean fullscreen) {
@@ -287,6 +317,7 @@ public void setFullscreen(boolean fullscreen) {
 
     /**
      * Toggles the window between maximized and normal states.
+     * Reverts to the explicitly set width and height if exiting the maximized state.
      * @param maximized True to maximize the window, false for normal size.
      */
     public void setMaximized(boolean maximized) {
@@ -301,22 +332,28 @@ public void setMaximized(boolean maximized) {
     }
 
     /**
-     * Adds a {@link Container} to the window using its intrinsic index.
+     * Adds a {@link Container} to the window using its intrinsically defined index.
      * @param container The container to add.
      */
     public void addContainer(Container container) {
         addContainer(container, container.getIndex());
     }
 
+    /**
+     * Adds a {@link Container} to the window using its intrinsic index, but associates it
+     * with a pre-compiled JavaFX Node. Useful for performance optimizations.
+     * @param container The container metadata and reference.
+     * @param node The pre-compiled JavaFX Node to render.
+     */
     public void addContainer(Container container, Node node) {
         addContainer(container, node, container.getIndex());
     }
 
     /**
      * 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.
+     * it recursively shifts existing containers up one index to accommodate the new one.
      * @param container The container to add.
-     * @param index The desired rendering index for the container.
+     * @param index The desired rendering index (z-order) for the container.
      */
     public void addContainer(Container container, int index) {
         Container current = containers.get(index);
@@ -329,6 +366,7 @@ public void addContainer(Container container, int index) {
         container.setWindow(this); // Store window reference.
 
         Node assemble;
+        // Check for cached node.
         if (container.getNode() != null) {
             assemble = container.getNode();
         } else {
@@ -347,29 +385,30 @@ public void addContainer(Container container, int index) {
     }
 
     /**
-     * Adds a pre-compiled {@link Container} to the window. Use {@link Container#assemble()} to build the {@link Node}. Nodes are automatically assembled when the base container is drawn to the screen.
-     * If a Container is large or executes long task it might freeze or lock the UI. You can assemble the Container asynchronously to prevent the UI from freezing.
+     * Adds a pre-compiled {@link Container} to the window at a specific index.
+     * Use {@link Container#assemble()} to build the {@link Node}. Nodes are automatically
+     * assembled when the base container is drawn to the screen.
+     * If a Container is large or executes a long task, it might freeze or lock the UI.
+     * You can assemble the Container asynchronously to prevent UI freezing.
      *
      * 
-     *     {@code
-     *     Container container = new EmptyContainer(100, 100);
-     *     // add elements to the container.
-     *     runTaskAsynchronously(() -> {
-     *         Node assemble = container.assemble();
-     *         // To draw nodes to the screen it must be called in the JavaFX thread.
-     *         Platform.runLater(() -> {
-     *          window.addContainer(container, assemble, 0); // Specifying the index is optional.
-     *         });
-     *     });
+     * {@code
+     * Container container = new EmptyContainer(100, 100);
+     * // Add elements to the container.
      *
-     *     // Display a loading view which can be removed in the task above.
-     *     }
+     * runTaskAsynchronously(() -> {
+     * Node assemble = container.assemble();
+     * Platform.runLater(() -> {
+     * window.addContainer(container, assemble, 0);
+     * });
+     * });
+     * // Display a loading view which can be removed in the task above.
+     * }
      * 
* - *
- * @param container The container to add. - * @param node The pre-compiled node to add. - * @param index The desired rendering index for the container. + * @param container The container context to track. + * @param node The pre-compiled node to visually add. + * @param index The desired rendering index (z-order) for the container. */ public void addContainer(Container container, Node node, int index) { Container current = containers.get(index); @@ -383,8 +422,8 @@ public void addContainer(Container container, Node node, int index) { } /** - * Adds all containers from the given TreeMap to this window's container collection. - * Existing containers with matching indices will be overwritten. + * Adds a collection of containers from the given TreeMap to this window. + * Existing containers with matching indices will be overwritten in the internal map. * @param con The TreeMap of containers to add. */ public void addContainers(TreeMap con) { @@ -393,15 +432,15 @@ public void addContainers(TreeMap con) { /** * Replaces the entire set of containers in the window with a new TreeMap of containers. - * @param containers The new TreeMap of containers. + * @param containers The new TreeMap of containers to track. */ 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. + * Replaces an existing container instance with a new container instance, preserving its original index. + * The old container must already exist in the window's collection for the replacement to occur. * @param oldContainer The container instance to be replaced. * @param newContainer The new container instance to take its place. */ @@ -413,7 +452,7 @@ public void replaceContainer(Container oldContainer, Container newContainer) { /** * Replaces the container at a specific index with a new container. - * The window is then re-rendered to reflect this change. + * The window is then re-rendered to reflect this visual change. * @param index The index at which to replace the container. * @param container The new container to place at the specified index. */ @@ -425,9 +464,9 @@ public void replaceContainer(int index, Container container) { /** * 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. + * Note: This removes the container from the internal map and its corresponding JavaFX + * Node from the root's children, but a subsequent `render()` call might be needed to + * ensure structural consistency depending on execution context. * @param container The container instance to remove. */ public void removeContainer(Container container) { @@ -447,8 +486,7 @@ public void removeContainer(Container container) { } /** - * Clears all containers from the window. - * A garbage collection hint is provided to the JVM. + * Clears all containers currently tracked by the window and removes them from the view. */ public void clearContainers() { new LinkedList<>(containers.values()).forEach(this::removeContainer); @@ -456,7 +494,7 @@ public void clearContainers() { } /** - * Removes the container at a specific index from the window and re-renders the display. + * Removes the container at a specific index from the window and triggers a re-render. * @param index The index of the container to remove. */ public void clearContainer(int index) { @@ -465,7 +503,7 @@ public void clearContainer(int index) { } /** - * Retrieves the TreeMap of all containers currently managed by the window. + * Retrieves the TreeMap of all containers currently managed by the window, ordered by index. * @return A TreeMap mapping container indices to Container objects. */ public TreeMap getContainers() { @@ -473,7 +511,7 @@ public TreeMap getContainers() { } /** - * Clears all containers and resets the window's root pane and scene. + * Clears all containers and resets the window's root pane and scene entirely. * The stage is not automatically shown after this operation. */ public void clear() { @@ -481,8 +519,8 @@ public void clear() { } /** - * Clears all containers and resets the window's root pane and scene. - * Optionally shows the stage after clearing. + * Clears all containers and resets the window's root pane and scene entirely. + * Optionally shows the stage after the clearance process is complete. * @param render True to show the stage after clearing, false otherwise. */ public void clear(boolean render) { @@ -498,7 +536,7 @@ public void clear(boolean render) { /** * Closes the JavaFX Stage associated with this window. - * A garbage collection hint is provided to the JVM. + * @param handleEvent If false, unbinds the window's hidden and close request event listeners before closing. */ public void close(boolean handleEvent) { if (stage != null) { @@ -511,23 +549,15 @@ public void close(boolean handleEvent) { } /** - * 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. + * Resets the visual state of the window by creating a new Stage with the original configuration parameters. */ 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. + * Fully constructs the JavaFX Stage structure and renders all active nodes onto the screen. */ public void buildAndRender() { buildStage(); @@ -535,10 +565,11 @@ public void buildAndRender() { } /** - * Builds and displays all active nodes on the screen. This function translates the engine's 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 nodes and updates the stage and scene. Focus is requested if the window is configured for it. *

* 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. + * modifying, or removing {@link Overlay} or {@link Container} objects to update the display state. *

*/ public void render() { @@ -550,8 +581,9 @@ public void render() { } /** - * 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. + * Translates the engine's container definitions into JavaFX nodes without immediately showing + * the stage, provided there is at least one active container. + * For most use cases, {@link #render()} is recommended. */ public void build() { if (!containers.isEmpty()) { @@ -560,9 +592,10 @@ public void build() { } /** - * 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. + * Iterates through active containers and applies them to the JavaFX root pane. + * This method processes and prepares containers for display. + * @param reset If true, fully reinitializes the root pane and scene graph (can cause flickering, but clears stale cache). + * If false, simply clears existing children and stylesheets before reapplying containers. */ public void build(boolean reset) { root.getChildren().clear(); @@ -579,9 +612,9 @@ public void build(boolean reset) { } /** - * 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. + * Adds a specific container on top of the current window's content stack. + * The container is dynamically assigned an index representing the highest available layer. + * @param container The container to assign and render. */ public void render(Container container) { int index = containers.isEmpty() ? 1 : containers.lastKey() + 1; @@ -591,9 +624,9 @@ public void render(Container 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. + * Assembles a single {@link Container} instance into a JavaFX Node and appends it to the window's root pane. + * Removes the previous node instance from the root if it existed. + * @param container The container to translate and render. */ private void renderContainer(Container container) { if (container.getNode() != null) { @@ -604,11 +637,9 @@ private void renderContainer(Container container) { } /** - * 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. + * Internal handler to render a container explicitly as a singular active popup. + * Removes the currently tracked popup container before registering the new one. + * @param container The container functioning as the popup layout. */ private void renderPopupContainer(Container container) { if (currentPopup != null) { @@ -619,16 +650,27 @@ private void renderPopupContainer(Container container) { addContainer(container); } + /** + * Computes positioning and renders a basic popup overlay dynamically onto the screen. + * @param overlay The {@link Overlay} content to embed inside the popup. + * @param position A predefined {@link PopupPosition} layout value. + * @param width The targeted width of the popup frame. + * @param height The targeted height of the popup frame. + * @param autoClose If true, the popup automatically dismisses itself after a set duration. + */ public void renderPopup(Overlay overlay, PopupPosition position, double width, double height, boolean autoClose) { renderPopup(overlay, position, width, height, autoClose, null); } /** - * Renders a popup with an overlay content, and desired position. - * This method calculates the popup's position based on window dimensions and scaling, - * then creates and renders a {@link Container} to house the overlay. - * @param overlay The {@link Overlay} content to display within the popup. - * @param position The desired position of the popup on the screen. + * Computes the X and Y coordinate logic for a popup based on the window's dimensions and scaling parameters, + * then delegates the drawing instructions to absolute coordinate rendering. + * @param overlay The {@link Overlay} logic. + * @param position The geographical screen layout {@link PopupPosition} logic. + * @param width Desired popup width. + * @param height Desired popup height. + * @param autoClose True if the popup should auto-dismiss on a timer. + * @param label An optional {@link TextOverlay} to accompany the popup. */ public void renderPopup(Overlay overlay, PopupPosition position, double width, double height, boolean autoClose, TextOverlay label) { // Get current window dimensions (these are in the logical, unscaled coordinate system) @@ -676,6 +718,17 @@ public void renderPopup(Overlay overlay, PopupPosition position, double width, d renderPopup(overlay, calculatedX, calculatedY, width, height, autoClose, label); } + /** + * Encapsulates an overlay into a dynamically constructed {@link EmptyContainer} positioned at exact coordinates, + * registering it as the active top-level popup element. Assigns lifecycle event hooks based on overlay types. + * @param overlay The {@link Overlay} element to draw. + * @param x The specific scaled horizontal X coordinate. + * @param y The specific scaled vertical Y coordinate. + * @param width The overall width boundary of the popup constraint container. + * @param height The overall height boundary of the popup constraint container. + * @param autoClose True to queue a removal thread via JavaFX timeline after 10,000 milliseconds. + * @param label Optional text overlay addition. + */ public void renderPopup(Overlay overlay, double x, double y, double width, double height, boolean autoClose, TextOverlay label) { EmptyContainer container = new EmptyContainer(x, y, width, height); container.setPrefSize(width, height); @@ -722,6 +775,14 @@ public void renderPopup(Overlay overlay, double x, double y, double width, doubl addContainer(container); } + /** + * Evaluates a pre-existing container directly into the application space as an exclusive top-level popup at specific coordinates. + * @param container The assembled container logic to set as active. + * @param x Translated X coordinate. + * @param y Translated Y coordinate. + * @param width Bounding box width. + * @param height Bounding box height. + */ public void renderPopup(Container container, double x, double y, double width, double height) { if (currentPopup != null) { removeContainer(currentPopup); @@ -737,6 +798,14 @@ public void renderPopup(Container container, double x, double y, double width, d addContainer(container); } + /** + * Handles complex inverse scaling math and layout configuration to translate a requested predefined {@link PopupPosition} + * onto exact coordinate mappings, then delegates the raw drawing instructions to the coordinate popup renderer. + * @param container The container acting as a custom popup view. + * @param position The conceptual mapping for the popup on screen. + * @param width The target display width. + * @param height The target display height. + */ public void renderPopup(Container container, PopupPosition position, double width, double height) { double windowWidth = this.width; double windowHeight = this.height; @@ -778,23 +847,46 @@ public void renderPopup(Container container, PopupPosition position, double widt } + /** + * Halts application workflow to trigger and display a system-level Alert dialog. + * @param alertOverlay The overlay containing the configured {@link Alert} structure. + */ public void renderAlert(AlertOverlay alertOverlay) { Alert alert = alertOverlay.getAlert(); alert.showAndWait(); } + /** + * Retrieves the instance of the current active popup container traversing the engine window layer. + * @return The currently displaying popup {@link Container}, or null if none exist. + */ public Container getCurrentPopup() { return currentPopup; } + /** + * Injects a custom window resize handler logic into the application window lifecycle. + * Overrides any previously configured resize hooks. + * @param windowResize The implementation defining custom resize handling logic. + */ public void onWindowResize(IWindowResize windowResize) { this.windowResize = windowResize; } + /** + * Retrieves the custom window resize handler currently bound to the window. + * @return The active {@link IWindowResize} implementation, or null if unassigned. + */ public IWindowResize getWindowResize() { return windowResize; } + /** + * Listens to the core JavaFX Stage dimensions to maintain structural integrity. + * If the scaling config is set to true, this dynamically applies affine transforms directly to the root pane + * to stretch the application content smoothly. Otherwise, it delegates resolution changes to a custom event dispatcher. + * @param stage The primary JavaFX Stage generating width/height property changes. + */ 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) -> { diff --git a/src/main/java/me/piitex/engine/WindowBuilder.java b/src/main/java/me/piitex/engine/WindowBuilder.java index e1433ce..728141f 100644 --- a/src/main/java/me/piitex/engine/WindowBuilder.java +++ b/src/main/java/me/piitex/engine/WindowBuilder.java @@ -7,13 +7,13 @@ /** * 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. + * This provides a more intuitive and flexible way to create {@link Window} instances + * compared to using multiple constructors with varying parameters. *
  * {@code
- * Window window = new WindowBuild("My Game")
+ * Window window = new WindowBuilder("My Game")
  * .setStageStyle(StageStyle.UNDECORATED)
- * setRoot(new Pane())
+ * .setRoot(new Pane())
  * .setDimensions(1280, 720)
  * .setBackgroundColor(Color.DARKBLUE)
  * .setFullscreen(true)
@@ -38,7 +38,7 @@ public class WindowBuilder {
     /**
      * Starts the building process for a new Window with a required title.
      *
-     * @param title The title of the window.
+     * @param title The process title and visible label of the window.
      */
     public WindowBuilder(String title) {
         this.title = title;
@@ -47,7 +47,7 @@ public WindowBuilder(String 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.
+     * @return The current WindowBuilder instance for method chaining.
      */
     public WindowBuilder setStageStyle(StageStyle stageStyle) {
         this.stageStyle = stageStyle;
@@ -55,9 +55,9 @@ public WindowBuilder setStageStyle(StageStyle stageStyle) {
     }
 
     /**
-     * Sets the scene root pane.
-     * @param root The pane type for the root.
-     * @return The current WindowBuild instance for chaining.
+     * Sets the scene root pane. This is the underlying base pane for the JavaFX scene.
+     * @param root The explicit {@link Pane} type to be used as the root.
+     * @return The current WindowBuilder instance for method chaining.
      */
     public WindowBuilder setRoot(Pane root) {
         this.root = root;
@@ -65,9 +65,9 @@ public WindowBuilder setRoot(Pane root) {
     }
 
     /**
-     * Sets the icon for the window.
-     * @param icon An {@link ImageLoader} for the window's icon.
-     * @return The current WindowBuild instance for chaining.
+     * Sets the window's taskbar and title bar icon.
+     * @param icon An {@link ImageLoader} instance mapping to the targeted window icon.
+     * @return The current WindowBuilder instance for method chaining.
      */
     public WindowBuilder setIcon(ImageLoader icon) {
         this.icon = icon;
@@ -75,10 +75,10 @@ public WindowBuilder setIcon(ImageLoader icon) {
     }
 
     /**
-     * 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.
+     * Sets the preferred width and height of the window upon initial rendering.
+     * @param width The target width in pixels.
+     * @param height The target height in pixels.
+     * @return The current WindowBuilder instance for method chaining.
      */
     public WindowBuilder setDimensions(double width, double height) {
         this.width = width;
@@ -87,9 +87,9 @@ public WindowBuilder setDimensions(double width, double height) {
     }
 
     /**
-     * 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.
+     * Sets the background color fill of the window's root pane and underlying scene.
+     * @param backgroundColor The JavaFX {@link Color} for the window's background.
+     * @return The current WindowBuilder instance for method chaining.
      */
     public WindowBuilder setBackgroundColor(Color backgroundColor) {
         this.backgroundColor = backgroundColor;
@@ -97,9 +97,9 @@ public WindowBuilder setBackgroundColor(Color backgroundColor) {
     }
 
     /**
-     * Sets whether the window should be in fullscreen mode.
-     * @param fullscreen True for fullscreen, false otherwise.
-     * @return The current WindowBuild instance for chaining.
+     * Sets whether the window should launch and maintain a borderless fullscreen mode.
+     * @param fullscreen True to enable fullscreen, false otherwise.
+     * @return The current WindowBuilder instance for method chaining.
      */
     public WindowBuilder setFullscreen(boolean fullscreen) {
         this.fullscreen = fullscreen;
@@ -107,9 +107,9 @@ public WindowBuilder setFullscreen(boolean fullscreen) {
     }
 
     /**
-     * Sets whether the window should be maximized on launch.
-     * @param maximized True to maximize, false otherwise.
-     * @return The current WindowBuild instance for chaining.
+     * Sets whether the window should launch already maximized across the user's primary display.
+     * @param maximized True to start maximized, false otherwise.
+     * @return The current WindowBuilder instance for method chaining.
      */
     public WindowBuilder setMaximized(boolean maximized) {
         this.maximized = maximized;
@@ -117,78 +117,126 @@ public WindowBuilder setMaximized(boolean maximized) {
     }
 
     /**
-     * Sets whether the window should be focused on launch.
-     * @param focused True to focus, false otherwise.
-     * @return The current WindowBuild instance for chaining.
+     * Sets whether the window will explicitly request OS focus to be brought to the front on launch.
+     * @param focused True to aggressively request focus, false otherwise.
+     * @return The current WindowBuilder instance for method chaining.
      */
     public WindowBuilder setFocused(boolean focused) {
         this.focused = focused;
         return this;
     }
 
+    /**
+     * Dictates whether the window's content automatically uses affine transformations
+     * to scale logically when the user resizes the window bounds.
+     * @param scale True to auto-scale components, false to handle resizing manually.
+     * @return The current WindowBuilder instance for method chaining.
+     */
     public WindowBuilder setScale(boolean scale) {
         this.scale = scale;
         return this;
     }
 
+    /**
+     * Toggles default JavaFX text and node anti-aliasing logic on or off.
+     * Turning this off can be useful for pixel-art style desktop applications.
+     * @param aliasing True to retain smoothing, false to enforce hard pixel edges.
+     * @return The current WindowBuilder instance for method chaining.
+     */
     public WindowBuilder setAntiAliasing(boolean aliasing) {
         this.antialiasing = aliasing;
         return this;
     }
 
     /**
-     * Constructs and returns a new {@link Window} object based on the builder's configurations.
-     * @return A new Window instance.
+     * Constructs and returns a fully initialized {@link Window} object based on the builder's stored configurations.
+     * @return A new runtime-ready Window instance.
      */
     public Window build() {
-        return new Window(this); // Calls the private constructor in the Window class
+        return new Window(this); // Calls the package-protected/public constructor in the Window class
     }
 
+    /**
+     * @return The configured window title.
+     */
     public String getTitle() {
         return title;
     }
 
+    /**
+     * @return The configured stage style.
+     */
     public StageStyle getStageStyle() {
         return stageStyle;
     }
 
+    /**
+     * @return The configured root pane instance.
+     */
     public Pane getRoot() {
         return root;
     }
 
+    /**
+     * @return The configured image loader logic for the window icon.
+     */
     public ImageLoader getIcon() {
         return icon;
     }
 
+    /**
+     * @return The configured launch width.
+     */
     public double getWidth() {
         return width;
     }
 
+    /**
+     * @return The configured launch height.
+     */
     public double getHeight() {
         return height;
     }
 
+    /**
+     * @return The configured background color fill.
+     */
     public Color getBackgroundColor() {
         return backgroundColor;
     }
 
+    /**
+     * @return True if fullscreen is enabled, false otherwise.
+     */
     public boolean isFullscreen() {
         return fullscreen;
     }
 
+    /**
+     * @return True if starting maximized, false otherwise.
+     */
     public boolean isMaximized() {
         return maximized;
     }
 
+    /**
+     * @return True if the window should steal focus upon loading.
+     */
     public boolean isFocused() {
         return focused;
     }
 
+    /**
+     * @return True if affine scaling transformations are enabled.
+     */
     public boolean isScale() {
         return scale;
     }
 
+    /**
+     * @return True if JavaFX text and node anti-aliasing should be retained.
+     */
     public boolean isAntialiasing() {
         return antialiasing;
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/me/piitex/engine/containers/Container.java b/src/main/java/me/piitex/engine/containers/Container.java
index eb0ba05..8401df4 100644
--- a/src/main/java/me/piitex/engine/containers/Container.java
+++ b/src/main/java/me/piitex/engine/containers/Container.java
@@ -12,22 +12,28 @@
 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.
+ * The container houses all the generic {@link me.piitex.engine.Element} items (like Overlays and Layouts)
+ * that are meant to render onto the parent {@link Window}. The class can be extended
+ * to support different containers that handle rendering in unique structural ways.
  * The default base container is the {@link EmptyContainer}. It does not have any special rendering properties.
  * 
  * {@code
- *      EmptyContainer container = new EmptyContainer(double width, double height);
- *      // Add elements to the container.
+ * EmptyContainer container = new EmptyContainer(double width, double height);
+ * // Add elements to the container.
  * }
  * 
*

- * The {@link Window} will have to render and handle the container. The two components work in unison. - *

+ * The {@link Window} will render and handle the container itself. The window specifically tracks Containers, + * which in turn track Elements. The two components work in unison to display the scene graph. + *

*
  * {@code
- *      Container container = new EmptyContainer(1920, 1080);
+ * Container container = new EmptyContainer(1920, 1080);
+ * window.addContainer(container); // Automaticallys draws to screen
  *
- *      window.addContainer(container);
+ * // Add elements directly to the container
+ * TextOverlay text = new TextOverlay("Overlay");
+ * container.addElement(text);
  * }
  * 
*/ @@ -36,6 +42,16 @@ public abstract class Container extends Renderer { private final List stylesheets = new ArrayList<>(); private final List renderEvents = new LinkedList<>(); + /** + * Constructs a new Container with specific positioning and dimensions. + * Inherited properties such as width and height are managed by the parent {@link Renderer}. + * + * @param view The base JavaFX Node that represents this container visually. + * @param x The initial horizontal (X) position relative to the parent window or root pane. + * @param y The initial vertical (Y) position relative to the parent window or root pane. + * @param width The explicitly requested width of the container. + * @param height The explicitly requested height of the container. + */ public Container(Node view, double x, double y, double width, double height) { setNode(view); this.x = x; @@ -46,6 +62,17 @@ public Container(Node view, double x, double y, double width, double height) { setHeight(height); } + /** + * Constructs a new Container with specific positioning, dimensions, and a distinct rendering index (z-order). + * Inherited properties such as width and height are managed by the parent {@link Renderer}. + * + * @param view The base JavaFX Node that represents this container visually. + * @param x The initial horizontal (X) position relative to the parent window or root pane. + * @param y The initial vertical (Y) position relative to the parent window or root pane. + * @param width The explicitly requested width of the container. + * @param height The explicitly requested height of the container. + * @param index The rendering index (z-order layer) of the container. Higher indices are rendered on top. + */ public Container(Node view, double x, double y, double width, double height, int index) { setNode(view); this.x = x; @@ -58,6 +85,7 @@ public Container(Node view, double x, double y, double width, double height, int } /** + * Retrieves the explicitly assigned horizontal position of the container. * @return The x position of the container in correlation to the window. */ public double getX() { @@ -65,8 +93,10 @@ public double getX() { } /** - * Set the x position of the container. - * @param x The x position. + * Sets the horizontal position of the container. + * This automatically updates the translation mapping of the underlying JavaFX Node. + * + * @param x The new x position coordinate. */ public void setX(double x) { this.x = x; @@ -74,6 +104,7 @@ public void setX(double x) { } /** + * Retrieves the explicitly assigned vertical position of the container. * @return The y position of the container in correlation to the window. */ public double getY() { @@ -81,33 +112,51 @@ public double getY() { } /** - * Set the y position of the container. - * @param y The y position. + * Sets the vertical position of the container. + * This automatically updates the translation mapping of the underlying JavaFX Node. + * + * @param y The new y position coordinate. */ public void setY(double y) { this.y = y; getNode().setTranslateY(y); } + /** + * Registers a custom render event listener to this container. + * These events hook into the container's lifecycle to execute logic during rendering phases. + * + * @param renderEvent The {@link IContainerRender} implementation to attach. Ignores null values. + */ public void addRenderEvent(IContainerRender renderEvent) { if (renderEvent != null) { this.renderEvents.add(renderEvent); } } + /** + * Unregisters a specific render event listener from this container. + * + * @param renderEvent The {@link IContainerRender} implementation to detach. Ignores null values. + */ public void removeRenderEvent(IContainerRender renderEvent) { if (renderEvent != null) { this.renderEvents.remove(renderEvent); } } + /** + * Retrieves the collection of all render event listeners currently attached to this container. + * + * @return A list of configured {@link IContainerRender} handlers. + */ public List getRenderEvents() { return renderEvents; } /** - * Gets all {@link Overlay}s added to the container. - * @return The current linked list of {@link Overlay}s. + * Filters the internal element tracking map to isolate and retrieve all child {@link Overlay} instances. + * @return A linked list containing all {@link Overlay}s registered to this container. */ public LinkedList getOverlays() { LinkedList toReturn = new LinkedList<>(); @@ -119,8 +168,9 @@ public LinkedList getOverlays() { } /** - * Gets all sub-containers for the container. - * @return The current linked list of sub-containers. + * Filters the internal element tracking map to isolate and retrieve all sub-containers. + * + * @return A linked list containing all nested {@link Container}s registered to this container. */ public LinkedList getContainers() { LinkedList toReturn = new LinkedList<>(); @@ -132,8 +182,9 @@ public LinkedList getContainers() { } /** - * Gets all {@link Layout}s for the container. - * @return The current linked list of {@link Layout}s + * Filters the internal element tracking map to isolate and retrieve all assigned {@link Layout} handlers. + * + * @return A linked list containing all {@link Layout} instances assigned to this container. */ public LinkedList getLayouts() { LinkedList toReturn = new LinkedList<>(); @@ -144,6 +195,13 @@ public LinkedList getLayouts() { return toReturn; } + /** + * Appends a local CSS stylesheet to the container's styling context. + * The provided file is automatically resolved into a compliant external URL format for JavaFX. + * + * @param file The local {@link File} object referencing the `.css` stylesheet. + * @throws RuntimeException If the file cannot be resolved into a structurally valid URL. + */ public void addStyleSheet(File file) { try { stylesheets.add(file.toURI().toURL().toExternalForm()); @@ -152,13 +210,21 @@ public void addStyleSheet(File file) { } } + /** + * Retrieves the collection of mapped CSS stylesheet URLs bound to this container. + * + * @return A list of formatted URL strings pointing to the stylesheets. + */ public List getStylesheets() { return stylesheets; } /** - * 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. + * Compiles and assembles the container, mapping the engine's logical API constraints + * onto the native JavaFX scene graph structure. Subclasses dictate exactly how child nodes + * are structurally arranged. + * + * @return The constructed JavaFX {@link Node} representing this container and its fully populated children. */ public abstract Node build(); } \ No newline at end of file diff --git a/src/main/java/me/piitex/engine/overlays/Overlay.java b/src/main/java/me/piitex/engine/overlays/Overlay.java index 1a39d36..6d5bf85 100644 --- a/src/main/java/me/piitex/engine/overlays/Overlay.java +++ b/src/main/java/me/piitex/engine/overlays/Overlay.java @@ -20,40 +20,40 @@ import java.util.List; /** - * An overlay is a visual element which can be rendered. The overlay class is the JavaFX equivalent of a {@link Node}. + * An overlay is a visual element which can be rendered. The overlay class is the framework's equivalent of a native JavaFX {@link Node}. * All overlays have generic events that are fired. For example, the {@link ElementClickEvent} is fired if the overlay is clicked. * *

* To render an overlay you first need to add it to a {@link Container}. The container will have to be managed to a {@link Window}. * The window is used to render the screen. *

- *     {@code
- *       // Create the overlay
- *       TextOverlay overlay = new TextOverlay("Text");
+ * {@code
+ * // Create the overlay
+ * TextOverlay overlay = new TextOverlay("Text");
  *
- *       // Create or fetch the container.
- *       Container container = new EmptyContainer(x, y, width, height, displayOrder);
+ * // Create or fetch the container.
+ * Container container = new EmptyContainer(x, y, width, height, displayOrder);
  *
- *       // Add the overlay to the container.
- *       container.addOverlay(overlay);
+ * // Add the overlay to the container's element map.
+ * container.addElement(overlay);
  *
- *       // Add the container to the window if needed.
- *       window.addContainer(container);
- *     }
+ * // Add the container to the window if needed.
+ * window.addContainer(container);
+ * }
  * 
* - * Handling overlay events are key to creation a functional game. During the rendering process, logical programming must be executed with events. + * Handling overlay events are key to creating a functional application. During the rendering process, logical programming must be executed with events. *
- *     {@code
- *       // Create the overlay
- *       TextOverlay overlay = new TextOverlay("Text");
+ * {@code
+ * // Create the overlay
+ * TextOverlay overlay = new TextOverlay("Text");
  *
- *       // Handle code when the overlay is clicked.
- *       overlay.onClick(event -> {
- *          // Handle logic
- *          System.out.println("The overlay was clicked!");
- *       });
- *     }
+ * // Handle code when the overlay is clicked.
+ * overlay.onClick(event -> {
+ * // Handle logic
+ * System.out.println("The overlay was clicked!");
+ * });
+ * }
  * 
*/ public abstract class Overlay extends Element { @@ -69,22 +69,42 @@ public abstract class Overlay extends Element { // Style classes. private final List styles = new LinkedList<>(); + /** + * Retrieves the explicit X coordinate offset of the overlay relative to its parent container. + * @return The horizontal layout position. + */ public double getX() { return x; } + /** + * Sets the explicit horizontal layout position of the overlay. + * Translates the underlying JavaFX node dynamically if it has already been assembled. + * + * @param x The new X layout constraint. + */ public void setX(double x) { this.x = x; if (getNode() != null) { - getNode().setTranslateY(x); + getNode().setTranslateX(x); // Note: Corrected to setTranslateX from setTranslateY } } + /** + * Retrieves the explicit Y coordinate offset of the overlay relative to its parent container. + * @return The vertical layout position. + */ public double getY() { return y; } + /** + * Sets the explicit vertical layout position of the overlay. + * Translates the underlying JavaFX node dynamically if it has already been assembled. + * + * @param y The new Y layout constraint. + */ public void setY(double y) { this.y = y; @@ -93,48 +113,97 @@ public void setY(double y) { } } + /** + * Retrieves the configured tooltip text meant to display when a user hovers over this overlay. + * @return The string tooltip data, or null if unassigned. + */ public String getTooltip() { return tooltip; } + /** + * Assigns a text-based tooltip to this overlay. The tooltip framework integrates this automatically + * into the JavaFX node during the assembly phase. + * + * @param tooltip The informative hover text to display. + */ public void setTooltip(String tooltip) { this.tooltip = tooltip; } + /** + * Registers a specialized submit event handler, primarily utilized by overlays capable of receiving input + * (e.g., text areas capturing an ENTER key press without shift modifiers). + * + * @param iOverlaySubmit The execution logic mapped to the submit event. + */ public void onOverlaySubmit(IOverlaySubmit iOverlaySubmit) { this.iOverlaySubmit = iOverlaySubmit; } + /** + * Retrieves the collection of local CSS stylesheet files assigned explicitly to this specific overlay. + * @return A list of {@link File} references for styles. + */ public List getStyleSheets() { return styleSheets; } + /** + * Links an external CSS file specifically to this overlay instance. + * @param file The targeted local {@link File}. + */ public void addStyleSheet(File file) { this.styleSheets.add(file); } + /** + * Attaches a native JavaFX CSS class name string to this overlay. + * @param style The CSS class definition. + */ public void addStyle(String style) { styles.add(style); } + /** + * Retrieves the collection of CSS style classes currently mapped to the overlay logic. + * @return A list of CSS class name strings. + */ public List getStyles() { return styles; } + /** + * @return The current configured hover cursor mapping. + */ + @Override public Cursor getCursor() { return cursor; } + /** + * Overrides the default mouse cursor visualization when the mouse pointer bounds enter the overlay. + * @param cursor The required JavaFX {@link Cursor}. + */ + @Override public void setCursor(Cursor cursor) { this.cursor = cursor; } /** - * Converts the overlay into a {@link Node} which is used for the JavaFX API. - * @return The converted {@link Node} for the overlay. + * Converts the logical engine overlay instructions exclusively into a raw {@link Node} which is used for the native JavaFX API. + * Subclasses define their specific node implementations here (e.g., ImageView, Text, TextField). + * + * @return The newly constructed native JavaFX {@link Node} representing the overlay data. */ protected abstract Node render(); + /** + * The primary assembly lifecycle phase. Generates the underlying node and safely enforces JavaFX + * UI thread checks before assigning structural input/control event wrappers like tooltips and key binds. + * + * @return The fully compiled and interactive {@link Node}. + */ @Override public Node assemble() { Node node = render(); @@ -155,6 +224,12 @@ public Node assemble() { return node; } + /** + * Injects standard input configuration, cursors, hover delays, and text area key binds directly + * onto the native node's event listener chains. This must be invoked exclusively on the JavaFX Application thread. + * + * @param node The physically rendering JavaFX node. + */ public void setInputControls(Node node) { if (cursor != null) { node.setCursor(cursor); From bffa15ab8def621bee7550b76270e7f8f79d29e4 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Wed, 6 May 2026 17:04:26 -0500 Subject: [PATCH 77/78] Renderer will now automatically draw background and border updates. --- src/main/java/me/piitex/engine/Renderer.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/me/piitex/engine/Renderer.java b/src/main/java/me/piitex/engine/Renderer.java index 79423e5..136d7a4 100644 --- a/src/main/java/me/piitex/engine/Renderer.java +++ b/src/main/java/me/piitex/engine/Renderer.java @@ -1,5 +1,7 @@ package me.piitex.engine; +import javafx.application.Platform; +import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.layout.*; import javafx.scene.paint.Color; @@ -217,6 +219,10 @@ public Color getBackgroundColor() { */ public void setBackgroundColor(Color backgroundColor) { this.backgroundColor = backgroundColor; + + if (Platform.isFxApplicationThread() && getNode() instanceof Pane pane) { + pane.setBackground(new Background(new BackgroundFill(getBackgroundColor(), CornerRadii.EMPTY, Insets.EMPTY))); + } } /** @@ -236,6 +242,10 @@ public Color getBorderColor() { */ public void setBorderColor(Color borderColor) { this.borderColor = borderColor; + + if (Platform.isFxApplicationThread() && getNode() instanceof Pane pane) { + pane.setBorder(new Border(new BorderStroke(borderColor, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT))); + } } /** From 0ceb256dc33d5e86c9b40f88c34e92b0dee52ba8 Mon Sep 17 00:00:00 2001 From: HackusatePvP Date: Wed, 6 May 2026 17:04:52 -0500 Subject: [PATCH 78/78] Fixed logical issue with GitHub request property. --- src/main/java/me/piitex/os/FileDownloader.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/piitex/os/FileDownloader.java b/src/main/java/me/piitex/os/FileDownloader.java index 58f6513..991d3ce 100644 --- a/src/main/java/me/piitex/os/FileDownloader.java +++ b/src/main/java/me/piitex/os/FileDownloader.java @@ -39,7 +39,9 @@ public FileDownloader() { */ public FileDownloader(boolean githubUrl) { this.executorService = Executors.newCachedThreadPool(); - addRequestProperty("Accept", "application/octet-stream"); + if (githubUrl) { + addRequestProperty("Accept", "application/octet-stream"); + } } /**