diff --git a/railroad-core/src/main/java/dev/railroadide/core/ui/collage/CanvasChangeListener.java b/railroad-core/src/main/java/dev/railroadide/core/ui/collage/CanvasChangeListener.java new file mode 100644 index 00000000..1a848fbe --- /dev/null +++ b/railroad-core/src/main/java/dev/railroadide/core/ui/collage/CanvasChangeListener.java @@ -0,0 +1,11 @@ +package dev.railroadide.core.ui.collage; + +/** + * A listener interface for receiving notifications about changes to the canvas. + */ +public interface CanvasChangeListener { + /** + * Invoked when the canvas has changed. + */ + void onCanvasChanged(); +} diff --git a/railroad-core/src/main/java/dev/railroadide/core/ui/collage/CollageCanvas.java b/railroad-core/src/main/java/dev/railroadide/core/ui/collage/CollageCanvas.java new file mode 100644 index 00000000..0c5d2ef2 --- /dev/null +++ b/railroad-core/src/main/java/dev/railroadide/core/ui/collage/CollageCanvas.java @@ -0,0 +1,160 @@ +package dev.railroadide.core.ui.collage; + +import javafx.scene.canvas.GraphicsContext; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * A canvas that holds collage elements and manages their state. + * @param the type of collage element + */ +public abstract class CollageCanvas { + private final List listeners = new ArrayList<>(); + private List elements; + private T selectedElement; + + /** + * Creates a new CollageCanvas with an empty list of elements. + */ + public CollageCanvas() { + this.elements = new ArrayList<>(); + } + + /** + * Adds an element to the canvas and notifies listeners of the change. + * @param element the element to add + */ + public void addElement(T element) { + this.elements.add(element); + notifyChangeListeners(); + } + + /** + * Removes an element from the canvas and notifies listeners of the change. + * @param element the element to remove + */ + public void removeElement(T element) { + this.elements.remove(element); + notifyChangeListeners(); + } + + /** + * Updates an existing element on the canvas and notifies listeners of the change. + * @param oldElement the element to update + * @param newElement the new element to replace the old one + */ + public void updateElement(T oldElement, T newElement) { + int index = elements.indexOf(oldElement); + if (index != -1) { + elements.set(index, newElement); + } + notifyChangeListeners(); + } + + /** + * Gets an unmodifiable list of elements on the canvas. + * @return the list of elements + */ + public List getElements() { + return Collections.unmodifiableList(elements); + } + + /** + * Clears all elements from the canvas and notifies listeners of the change. + */ + public void clear() { + this.elements.clear(); + this.selectedElement = null; + notifyChangeListeners(); + } + + /** + * Adds a listener to be notified when the canvas changes. + * @param listener the listener to add + */ + public void addChangeListener(CanvasChangeListener listener) { + this.listeners.add(listener); + } + + /** + * Removes a listener from the canvas. + * @param listener the listener to remove + */ + public void removeChangeListener(CanvasChangeListener listener) { + this.listeners.remove(listener); + } + + /** + * Notifies all registered listeners of a change to the canvas. + */ + public void notifyChangeListeners() { + for (CanvasChangeListener listener : listeners) { + listener.onCanvasChanged(); + } + } + + /** + * Sets the selected element on the canvas and notifies listeners of the change. + * @param element the element to select + */ + public void setSelectedElement(T element) { + this.selectedElement = element; + notifyChangeListeners(); + } + + /** + * Gets the currently selected element on the canvas. + * @return an Optional containing the selected element, or empty if no element is selected + */ + public Optional getSelectedElement() { + return Optional.ofNullable(this.selectedElement); + } + + /** + * Gets the element at the specified coordinates, if any. + * @param x the x coordinate + * @param y the y coordinate + * @return an Optional containing the element at the specified coordinates, or empty if no element is found + */ + public Optional getElementFromPosition(int x, int y) { + for (T element : elements) { + if (x < element.getX() || x > element.getX() + element.getWidth()) continue; + if (y < element.getY() || y > element.getY() + element.getHeight()) continue; + return Optional.of(element); + } + + return Optional.empty(); + } + + /** + * Renders the selection box around the selected element, if any. + * @param gc the GraphicsContext to render with + */ + public void render(GraphicsContext gc) { + if (getSelectedElement().isPresent()) { + T selected = getSelectedElement().get(); + var box = new Rectangle(selected.getX(), selected.getY(), selected.getWidth(), selected.getHeight()); + renderSelectionBox(gc, box); + } + } + + /** + * Renders a selection box around the given rectangle. + * @param gc the GraphicsContext to render with + * @param box the rectangle to draw the selection box around + */ + void renderSelectionBox(GraphicsContext gc, Rectangle box) { + gc.setStroke(Color.color(0.23, 1, 0.34)); // TODO Use theme colour? + gc.setLineWidth(3); + final double x = box.getX() - gc.getLineWidth() + 1; + final double y = box.getY() - gc.getLineWidth() + 1; + final double w = box.getWidth() + gc.getLineWidth(); + final double h = box.getHeight() + gc.getLineWidth(); + gc.strokeRect(x, y, w, h); + } +} diff --git a/railroad-core/src/main/java/dev/railroadide/core/ui/collage/CollageElement.java b/railroad-core/src/main/java/dev/railroadide/core/ui/collage/CollageElement.java new file mode 100644 index 00000000..5ca945fa --- /dev/null +++ b/railroad-core/src/main/java/dev/railroadide/core/ui/collage/CollageElement.java @@ -0,0 +1,19 @@ +package dev.railroadide.core.ui.collage; + +import javafx.scene.canvas.GraphicsContext; + +/** + * A collage element that can be rendered on a canvas. + */ +public interface CollageElement { + int getX(); + int getY(); + int getWidth(); + int getHeight(); + + /** + * Renders the element on the given GraphicsContext. + * @param gc the GraphicsContext to render on + */ + void render(GraphicsContext gc); +} diff --git a/src/main/java/dev/railroadide/railroad/ide/IDESetup.java b/src/main/java/dev/railroadide/railroad/ide/IDESetup.java index 543dfdac..e5657921 100644 --- a/src/main/java/dev/railroadide/railroad/ide/IDESetup.java +++ b/src/main/java/dev/railroadide/railroad/ide/IDESetup.java @@ -7,11 +7,13 @@ import dev.railroadide.core.settings.keybinds.KeybindContexts; import dev.railroadide.core.settings.keybinds.KeybindData; import dev.railroadide.core.ui.*; +import dev.railroadide.railroad.ide.features.gui_generation.GuiCollage; import dev.railroadide.core.ui.localized.LocalizedCheckMenuItem; import dev.railroadide.core.ui.localized.LocalizedLabel; import dev.railroadide.core.ui.localized.LocalizedMenu; import dev.railroadide.core.ui.localized.LocalizedMenuItem; import dev.railroadide.railroad.Railroad; +import dev.railroadide.railroad.ide.features.gui_generation.GuiBackgroundGenerator; import dev.railroadide.railroad.ide.projectexplorer.ProjectExplorerPane; import dev.railroadide.railroad.ide.ui.ConsolePane; import dev.railroadide.railroad.ide.ui.IDEWelcomePane; @@ -43,6 +45,9 @@ import org.kordamp.ikonli.fontawesome6.FontAwesomeSolid; import org.kordamp.ikonli.javafx.FontIcon; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.IOException; import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.Locale; @@ -138,6 +143,23 @@ private static MenuBar createMenuBar() { newFileItem.setGraphic(new FontIcon(FontAwesomeSolid.FILE)); newFileItem.setKeybindData(new KeybindData(KeyCode.N, new KeyCombination.Modifier[]{KeyCombination.SHORTCUT_DOWN})); + var guiTestItem = new MenuItem("Open GUI Test Window"); + guiTestItem.setOnAction($ -> { + // TODO move to File -> New -> GUI & take in input for type, size etc + var testStage = new Stage(); + testStage.setTitle("GUI Test Window"); + BufferedImage refImage = null; + try { + refImage = ImageIO.read(Path.of("D:\\template.png").toFile()); + } catch (IOException e) { + throw new RuntimeException(e); + } + var testPane = new GuiCollage(GuiBackgroundGenerator.builder().setHeight(158).setWidth(176).setReferenceImage(refImage).build()); + var scene = new Scene(testPane, 800, 600); + testStage.setScene(scene); + testStage.show(); + }); + var openFileItem = new LocalizedMenuItem("railroad.menu.file.open_file"); openFileItem.setGraphic(new FontIcon(FontAwesomeSolid.FOLDER_OPEN)); openFileItem.setAccelerator(new KeyCodeCombination(KeyCode.O, KeyCombination.SHORTCUT_DOWN)); @@ -255,6 +277,7 @@ private static MenuBar createMenuBar() { fileMenu.getItems().add(saveAsItem); fileMenu.getItems().add(separator1); fileMenu.getItems().add(exitItem); + fileMenu.getItems().add(guiTestItem); fileMenu.getStyleClass().add("rr-menu"); var editMenu = new LocalizedMenu("railroad.menu.edit"); diff --git a/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiBackgroundGenerator.java b/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiBackgroundGenerator.java new file mode 100644 index 00000000..f1e813be --- /dev/null +++ b/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiBackgroundGenerator.java @@ -0,0 +1,329 @@ +package dev.railroadide.railroad.ide.features.gui_generation; + +import dev.railroadide.railroad.Railroad; +import javafx.embed.swing.SwingFXUtils; +import javafx.scene.image.WritableImage; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.net.URL; + +/** + * A class for generating GUI backgrounds based on a reference image. + * TODO - Allow for removal and customization of inventory textures, including custom sizing. + * Render title? "Do not redistribute our games or any alterations of our games or game files" + */ +public class GuiBackgroundGenerator { + + private GuiBackgroundGenerator() { + } + + /** + * Creates a new builder instance for generating a GUI background. + * @return A new Builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private int width; + private int height; + private BufferedImage referenceImage; + + /** + * Sets the width of the GUI background to be generated. + * @param width The desired width in pixels. + * @return The builder instance + */ + public Builder setWidth(int width) { + this.width = width; + return this; + } + + /** + * Sets the height of the GUI background to be generated. + * @param height The desired height in pixels. + * @return The builder instance + */ + public Builder setHeight(int height) { + this.height = height; + return this; + } + + /** + * Sets the reference image to be used for generating the GUI background. + * @param referenceImage The reference image as a BufferedImage. + * @return The builder instance + */ + public Builder setReferenceImage(BufferedImage referenceImage) { + this.referenceImage = referenceImage; + return this; + } + + /** + * Generates a GuiReference object by extracting necessary components from the reference image. + * @return A GuiReference object containing the extracted components. + */ + private GuiReference generateReference() { + var topSide = this.referenceImage.getSubimage(4, 0, 1, 3); + var rightSide = this.referenceImage.getSubimage(197, 3, 3, 1); + var leftSide = this.referenceImage.getSubimage(0, 3, 3, 1); + var topLeft = this.referenceImage.getSubimage(0, 0, 4, 4); + var topRight = this.referenceImage.getSubimage(197, 0, 3, 3); + + var bottomLeftOuterCorner = this.referenceImage.getSubimage(0, 134, 3, 3); + var bottomRightOuterCorner = this.referenceImage.getSubimage(197, 134, 3, 3); + var bottomLeftInnerCorner = this.referenceImage.getSubimage(12, 134, 3, 4); + var bottomRightInnerCorner = this.referenceImage.getSubimage(185, 134, 3, 4); + var bottomLeftEdge = this.referenceImage.getSubimage(3, 134, 1, 3); + var bottomRightEdge = this.referenceImage.getSubimage(196, 134, 1, 3); + + var bottomPadding = this.referenceImage.getSubimage(15, 134, 170, 4); + var background = this.referenceImage.getSubimage(3, 3, 194, 131); + + return new GuiReference( + topSide, + rightSide, leftSide, + topLeft, topRight, + bottomLeftOuterCorner, bottomRightOuterCorner, + bottomLeftInnerCorner, bottomRightInnerCorner, + bottomLeftEdge, bottomRightEdge, + bottomPadding, background); + } + + /** + * Draws the provided image onto the graphics context at the specified coordinates. + * @param g The Graphics2D context to draw on. + * @param img The image to be drawn. + * @param x1 The starting x-coordinate. + * @param y1 The starting y-coordinate. + * @param x2 The ending x-coordinate. + * @param y2 The ending y-coordinate. + */ + private void draw(Graphics2D g, BufferedImage img, int x1, int y1, int x2, int y2) { + g.drawImage(img, x1, y1, x2, y2, 0, 0, img.getWidth(), img.getHeight(), null); + } + + /** + * Draws the top section of the GUI background. + * @param g The Graphics2D context to draw on. + * @param ref The GuiReference containing the components to be drawn. + */ + private void drawTop(Graphics2D g, GuiReference ref) { + //Top Side + draw(g, ref.topSide(), + ref.topLeft().getWidth(), + 0, + this.width - ref.topRight().getWidth(), + ref.topSide().getHeight()); + + //Top Left corner + draw(g, ref.topLeft(), + 0, + 0, + ref.topLeft().getWidth(), + ref.topLeft().getHeight()); + + //Top Right corner + draw(g, ref.topRight(), + this.width - ref.topRight().getWidth(), + 0, + this.width, + ref.topRight().getHeight()); + } + + /** + * Draws the sides of the GUI background. + * @param g The Graphics2D context to draw on. + * @param ref The GuiReference containing the components to be drawn. + * @param expanded If the sides need to be taller due to the GUI being 176px wide + */ + private void drawSides(Graphics2D g, GuiReference ref, boolean expanded) { + //Left Side + draw(g, ref.leftSide(), + 0, + ref.topLeft().getHeight(), + ref.leftSide().getWidth(), + expanded ? this.height - ref.bottomPadding().getHeight() : this.height); + + //Right Side + draw(g, ref.rightSide(), + this.width - ref.rightSide().getWidth(), + ref.topRight().getHeight(), + this.width, + expanded ? this.height - ref.bottomPadding().getHeight() : this.height); + } + + /** + * Draws the outer corners of the GUI background. + * @param g The Graphics2D context to draw on. + * @param ref The GuiReference containing the components to be drawn. + */ + private void drawOuterCorners(Graphics2D g, GuiReference ref) { + //Left Outer Corner + draw(g, ref.bottomLeftOuterCorner(), + 0, + this.height - ref.bottomLeftOuterCorner().getHeight() - 1, + ref.bottomLeftOuterCorner().getWidth(), + this.height - 1); + + //Right Outer Corner + draw(g, ref.bottomRightOuterCorner(), + this.width - ref.bottomRightOuterCorner().getWidth(), + this.height - ref.bottomRightOuterCorner().getHeight() - 1, + this.width, + this.height - 1); + } + + /** + * Draws the inner corners of the GUI background. + * @param g The Graphics2D context to draw on. + * @param ref The GuiReference containing the components to be drawn. + */ + private void drawInnerCorners(Graphics2D g, GuiReference ref) { + //Left Inner Corner + draw(g, ref.bottomLeftInnerCorner(), + (this.width - 176) / 2, + this.height - ref.bottomLeftInnerCorner().getHeight(), + (this.width - 176) / 2 + ref.bottomLeftInnerCorner().getWidth(), + this.height); + + //Right Inner Corner + draw(g, ref.bottomRightInnerCorner(), + this.width - ((this.width - 176) / 2) - ref.bottomRightInnerCorner().getWidth(), + this.height - ref.bottomRightInnerCorner().getHeight(), + this.width - ((this.width - 176) / 2), + this.height); + } + + /** + * Draws the corner edges of the GUI background. + * @param g The Graphics2D context to draw on. + * @param ref The GuiReference containing the components to be drawn. + */ + private void drawCornerEdges(Graphics2D g, GuiReference ref) { + //Left Edge + draw(g, ref.bottomLeftEdge(), + ref.bottomLeftOuterCorner().getWidth(), + this.height - ref.bottomLeftEdge().getHeight() - 1, + ((this.width - 176) / 2) + 1, + this.height - 1); + + //Right Edge + draw(g, ref.bottomRightEdge(), + this.width - ((this.width - 176) / 2), + this.height - ref.bottomRightEdge().getHeight() - 1, + this.width - ref.bottomRightOuterCorner().getWidth(), + this.height - 1); + } + + /** + * Draws the corners of the GUI background, including outer corners, inner corners and corner edges. + * @param g The Graphics2D context to draw on. + * @param ref The GuiReference containing the components to be drawn. + * @param needsSpacing If the inner corners and corner edges are needed. + */ + private void drawCorners(Graphics2D g, GuiReference ref, boolean needsSpacing) { + drawOuterCorners(g, ref); + if (needsSpacing) { + drawInnerCorners(g, ref); + drawCornerEdges(g, ref); + } + } + + /** + * Draws the bottom padding of the GUI background. + * @param g The Graphics2D context to draw on. + * @param ref The GuiReference containing the components to be drawn. + * @param expanded If the padding needs to be wider or not. + */ + private void drawBottomPadding(Graphics2D g, GuiReference ref, boolean expanded) { + //Bottom Padding + if (expanded) { + draw(g, ref.bottomPadding(), + ((this.width - 176) / 2) + ref.bottomLeftInnerCorner().getWidth(), + this.height - ref.bottomPadding().getHeight() - 1, + this.width - ((this.width - 176) / 2) - ref.bottomRightInnerCorner().getWidth(), + this.height); + } else { + draw(g, ref.bottomPadding(), + ref.bottomLeftOuterCorner().getWidth(), + this.height - ref.bottomPadding().getHeight() - 1, + this.width - ref.bottomRightOuterCorner().getWidth(), + this.height); + } + } + + /** + * Draws the background of the GUI. + * @param g The Graphics2D context to draw on. + * @param ref The GuiReference containing the components to be drawn. + */ + private void drawBackground(Graphics2D g, GuiReference ref) { + draw(g, ref.background(), + ref.leftSide().getWidth(), + ref.topSide().getHeight(), + this.width - ref.rightSide().getWidth(), + this.height - ref.bottomPadding().getHeight()); + } + + /** + * Draws the GUI background based on the provided GuiReference and the specified width and height. + * @param reference The GuiReference containing the components to be used for drawing. + * @return A BufferedImage representing the drawn GUI background. + */ + private BufferedImage drawGuiBackground(GuiReference reference) { + var result = new BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = result.createGraphics(); + + drawTop(graphics, reference); + drawSides(graphics, reference, this.width > 176); + + // If the inner corners and corner edges are needed. + boolean wider = this.width > 176 + + reference.bottomLeftOuterCorner().getWidth() + + reference.bottomRightOuterCorner().getWidth(); + + if (this.width > 176) + drawCorners(graphics, reference, wider); + + drawBottomPadding(graphics, reference, wider); + drawBackground(graphics, reference); + + graphics.dispose(); + return result; + } + + /** + * Builds the GUI background image based on the specified parameters and reference image. + * @return A WritableImage representing the generated GUI background. + */ + public WritableImage build() { + try { + if (this.referenceImage == null) { + throw new IllegalStateException("Reference image must be set before building the GUI background."); + } + var textureResult = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB); + + URL inventoryImageUrl = Railroad.getResource("/inventory_textures/background/gui_inventory.png"); + BufferedImage inventoryImage = ImageIO.read(inventoryImageUrl); + + var guiTexture = drawGuiBackground(generateReference()); + + var textureGraphics = textureResult.createGraphics(); + textureGraphics.drawImage(guiTexture, 0, 0, null); + textureGraphics.drawImage(inventoryImage, + (guiTexture.getWidth() - inventoryImage.getWidth()) / 2, height, + null); + textureGraphics.dispose(); + + return SwingFXUtils.toFXImage(textureResult, null); + } catch (Exception e) { + Railroad.LOGGER.error("Failed to load inventory texture for GUI background generation! {}", e); + return null; + } + } + } +} diff --git a/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiCollage.java b/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiCollage.java new file mode 100644 index 00000000..4592ffe1 --- /dev/null +++ b/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiCollage.java @@ -0,0 +1,128 @@ +package dev.railroadide.railroad.ide.features.gui_generation; + +import dev.railroadide.core.ui.RRStackPane; +import dev.railroadide.core.ui.collage.CollageCanvas; +import javafx.scene.Group; +import javafx.scene.canvas.Canvas; +import javafx.scene.canvas.GraphicsContext; +import javafx.scene.image.Image; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.ScrollEvent; +import javafx.scene.paint.Paint; +import javafx.scene.transform.Scale; + +import java.util.concurrent.atomic.AtomicReference; + +public class GuiCollage extends RRStackPane { + private double scale = 1.0; + private double lastMouseX = 0; + private double lastMouseY = 0; + + private final CollageCanvas canvas; + private final Canvas fxCanvas = new Canvas(256, 256); + private final Group group = new Group(fxCanvas); + private final Scale scaleTransform = new Scale(scale, scale, 0, 0); + private final AtomicReference dragging = new AtomicReference<>(); + + private void onScroll(ScrollEvent event) { + //TODO fix zooming. Should also be hooked into the entire window, not just the canvas. + if (event.isControlDown()) { + double zoomFactor = 1.05; + if (event.getDeltaY() < 0) { + zoomFactor = 1 / zoomFactor; + } + double oldScale = scale; + scale *= zoomFactor; + + double f = (scale / oldScale) - 1; + + double dx = event.getX() - group.getBoundsInParent().getMinX(); + double dy = event.getY() - group.getBoundsInParent().getMinY(); + + group.setTranslateX(group.getTranslateX() - f * dx); + group.setTranslateY(group.getTranslateY() - f * dy); + + scaleTransform.setX(scale); + scaleTransform.setY(scale); + } + } + + private void onMouseClicked(MouseEvent event) { + // TODO quick edit menu, such as delete, open texture folder. + if (event.getButton() == MouseButton.SECONDARY) return; + if (dragging.get() != null) { + dragging.set(null); + } + var clicked = canvas.getElementFromPosition((int) event.getX(), (int) event.getY()); + clicked.ifPresent(this.canvas::setSelectedElement); + } + + private void onMousePressed(MouseEvent event) { + if (event.isSecondaryButtonDown()) { + lastMouseX = event.getSceneX(); + lastMouseY = event.getSceneY(); + } else { + var clicked = canvas.getElementFromPosition((int) event.getX(), (int) event.getY()); + dragging.set(clicked.orElse(null)); + } + } + + private void onMouseDragged(MouseEvent event) { + if (event.isSecondaryButtonDown()) { + //TODO also handle this in the entire window, not just the canvas? + double deltaX = event.getSceneX() - lastMouseX; + double deltaY = event.getSceneY() - lastMouseY; + + group.setTranslateX(group.getTranslateX() + deltaX); + group.setTranslateY(group.getTranslateY() + deltaY); + + lastMouseX = event.getSceneX(); + lastMouseY = event.getSceneY(); + } else { + if (dragging.get() == null) return; + int offsetX = (int) (event.getX() - dragging.get().getX()); + int offsetY = (int) (event.getY() - dragging.get().getY()); + dragging.get().setPosition((int) event.getX() + offsetX, (int) event.getY() + offsetY); + + canvas.notifyChangeListeners(); + } + } + + public GuiCollage(Image background) { + super(); + + canvas = new CollageCanvas<>() { + @Override + public void render(GraphicsContext gc) { + fxCanvas.getGraphicsContext2D().clearRect(0, 0, fxCanvas.getWidth(), fxCanvas.getHeight()); + fxCanvas.getGraphicsContext2D().setFill(Paint.valueOf("#2b2b2b")); //TODO get from theme + fxCanvas.getGraphicsContext2D().fillRect(0, 0, fxCanvas.getWidth(), fxCanvas.getHeight()); + fxCanvas.getGraphicsContext2D().drawImage(background, + 0, 0, + background.getWidth(), background.getHeight()); + + super.render(gc); + for (GuiCollageElement element : this.getElements()) { + element.render(fxCanvas.getGraphicsContext2D()); + } + } + }; + + fxCanvas.getGraphicsContext2D().setImageSmoothing(false); + + this.canvas.addChangeListener(() -> { + this.canvas.render(this.fxCanvas.getGraphicsContext2D()); + }); + + group.getTransforms().add(scaleTransform); + this.getChildren().add(group); + + this.fxCanvas.setOnScroll(this::onScroll); + this.fxCanvas.setOnMouseClicked(this::onMouseClicked); + this.fxCanvas.setOnMousePressed(this::onMousePressed); + this.fxCanvas.setOnMouseDragged(this::onMouseDragged); + + this.canvas.render(this.fxCanvas.getGraphicsContext2D()); + } +} diff --git a/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiCollageElement.java b/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiCollageElement.java new file mode 100644 index 00000000..40cd2874 --- /dev/null +++ b/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiCollageElement.java @@ -0,0 +1,74 @@ +package dev.railroadide.railroad.ide.features.gui_generation; + +import dev.railroadide.core.ui.collage.CollageElement; +import javafx.scene.canvas.GraphicsContext; +import javafx.scene.image.Image; + +import java.io.File; + +/** + * A GUI element that can be added to a collage. + * This element is used for inventory GUIs. + */ +public class GuiCollageElement implements CollageElement { + private int x; + private int y; + private int width; + private int height; + private String resourceLocation; + //TODO Make resource location configurable, and add support for options for the element. + + /** + * Creates a new GuiCollageElement with the specified position and size. + * @param x X position + * @param y Y position + * @param width Width + * @param height Height + */ + public GuiCollageElement(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.resourceLocation = "D:\\item_slot.png"; + } + + @Override + public int getX() { + return x; + } + + @Override + public int getY() { + return y; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + /** + * Renders the GUI element on the provided GraphicsContext. + * @param gc the GraphicsContext to render on + */ + @Override + public void render(GraphicsContext gc) { + gc.drawImage(new Image(new File(this.resourceLocation).toURI().toString()), getX(), getY()); + } + + /** + * Sets the position of the GUI element. + * @param x X position + * @param y Y position + */ + public void setPosition(int x, int y) { + this.x = x; + this.y = y; + } +} diff --git a/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiReference.java b/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiReference.java new file mode 100644 index 00000000..88c080fe --- /dev/null +++ b/src/main/java/dev/railroadide/railroad/ide/features/gui_generation/GuiReference.java @@ -0,0 +1,27 @@ +package dev.railroadide.railroad.ide.features.gui_generation; + +import java.awt.image.BufferedImage; + +/** + * @param topSide TODO work out if all sides/edges should be fully captured or just 3x1 + * @param rightSide + * @param leftSide + * @param topLeft + * @param topRight + * @param bottomLeftOuterCorner + * @param bottomRightOuterCorner + * @param bottomLeftInnerCorner + * @param bottomRightInnerCorner + * @param bottomLeftEdge + * @param bottomRightEdge + * @param bottomPadding + * @param background + */ +public record GuiReference(BufferedImage topSide, BufferedImage rightSide, BufferedImage leftSide, + BufferedImage topLeft, BufferedImage topRight, + BufferedImage bottomLeftOuterCorner, BufferedImage bottomRightOuterCorner, + BufferedImage bottomLeftInnerCorner, BufferedImage bottomRightInnerCorner, + BufferedImage bottomLeftEdge, BufferedImage bottomRightEdge, + BufferedImage bottomPadding, + BufferedImage background) { +} diff --git a/src/main/resources/assets/railroad/inventory_textures/background/gui_inventory.png b/src/main/resources/assets/railroad/inventory_textures/background/gui_inventory.png new file mode 100644 index 00000000..7afb3747 Binary files /dev/null and b/src/main/resources/assets/railroad/inventory_textures/background/gui_inventory.png differ diff --git a/src/main/resources/assets/railroad/inventory_textures/item_slot.png b/src/main/resources/assets/railroad/inventory_textures/item_slot.png new file mode 100644 index 00000000..0a0d0d3d Binary files /dev/null and b/src/main/resources/assets/railroad/inventory_textures/item_slot.png differ