From 23a95cd137d4d9d831f3d2856b46fd25dc557763 Mon Sep 17 00:00:00 2001 From: extio-de Date: Sun, 28 Dec 2025 11:53:59 +0100 Subject: [PATCH] feat(theme): add Blueprint theme and corresponding pattern renderer --- .../renderer/g2d/G2DAutoConfiguration.java | 6 + .../g2d/theme/BlueprintPatternRenderer.java | 129 ++++++++++++++++++ .../g2d/theme/BlueprintThemeFactoryBean.java | 46 +++++++ 3 files changed, 181 insertions(+) create mode 100644 src/main/java/de/extio/game_engine/renderer/g2d/theme/BlueprintPatternRenderer.java create mode 100644 src/main/java/de/extio/game_engine/renderer/g2d/theme/BlueprintThemeFactoryBean.java diff --git a/src/main/java/de/extio/game_engine/renderer/g2d/G2DAutoConfiguration.java b/src/main/java/de/extio/game_engine/renderer/g2d/G2DAutoConfiguration.java index 86bb8ff..1353bdf 100644 --- a/src/main/java/de/extio/game_engine/renderer/g2d/G2DAutoConfiguration.java +++ b/src/main/java/de/extio/game_engine/renderer/g2d/G2DAutoConfiguration.java @@ -14,6 +14,7 @@ import de.extio.game_engine.renderer.g2d.theme.G2DThemeManager; import de.extio.game_engine.renderer.g2d.theme.PatternRenderer; import de.extio.game_engine.renderer.g2d.theme.BevelDarkThemeFactoryBean; import de.extio.game_engine.renderer.g2d.theme.BevelLightThemeFactoryBean; +import de.extio.game_engine.renderer.g2d.theme.BlueprintThemeFactoryBean; import de.extio.game_engine.renderer.g2d.theme.ContemporaryThemeFactoryBean; import de.extio.game_engine.renderer.g2d.theme.DreamThemeFactoryBean; import de.extio.game_engine.renderer.g2d.theme.FantasyThemeFactoryBean; @@ -104,6 +105,11 @@ public class G2DAutoConfiguration { return new BevelDarkThemeFactoryBean(); } + @Bean + FactoryBean blueprintTheme() { + return new BlueprintThemeFactoryBean(); + } + @Bean G2DThemeManager g2dThemeManager( final StaticResourceService staticResourceService, diff --git a/src/main/java/de/extio/game_engine/renderer/g2d/theme/BlueprintPatternRenderer.java b/src/main/java/de/extio/game_engine/renderer/g2d/theme/BlueprintPatternRenderer.java new file mode 100644 index 0000000..a0e74f6 --- /dev/null +++ b/src/main/java/de/extio/game_engine/renderer/g2d/theme/BlueprintPatternRenderer.java @@ -0,0 +1,129 @@ +package de.extio.game_engine.renderer.g2d.theme; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Stroke; + +import org.springframework.context.annotation.Conditional; +import org.springframework.stereotype.Component; + +import de.extio.game_engine.renderer.g2d.G2DRendererCondition; +import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont; + +/** + * Implementation of PatternRenderer that draws a technical "Blueprint" style with grids and measurement markers. + */ +@Conditional(G2DRendererCondition.class) +@Component +public class BlueprintPatternRenderer implements PatternRenderer { + + @Override + public void drawDecorativeBorder(final Graphics2D g2d, final int x, final int y, final int width, final int height, final int strength, final Color color) { + g2d.setColor(color); + + // Main thin border line + final Stroke oldStroke = g2d.getStroke(); + g2d.setStroke(new BasicStroke(1.0f)); + g2d.drawRect(x, y, width - 1, height - 1); + + // Corner accents (thicker) + final int cornerLen = Math.min(Math.min(width, height) / 3, 20); + final int thick = Math.max(2, strength); + + g2d.fillRect(x, y, cornerLen, thick); // Top-Left Horz + g2d.fillRect(x, y, thick, cornerLen); // Top-Left Vert + + g2d.fillRect(x + width - cornerLen, y, cornerLen, thick); // Top-Right Horz + g2d.fillRect(x + width - thick, y, thick, cornerLen); // Top-Right Vert + + g2d.fillRect(x, y + height - thick, cornerLen, thick); // Bottom-Left Horz + g2d.fillRect(x, y + height - cornerLen, thick, cornerLen); // Bottom-Left Vert + + g2d.fillRect(x + width - cornerLen, y + height - thick, cornerLen, thick); // Bottom-Right Horz + g2d.fillRect(x + width - thick, y + height - cornerLen, thick, cornerLen); // Bottom-Right Vert + + // Mid-point markers + final int midX = x + width / 2; + final int midY = y + height / 2; + final int markerLen = 6; + + g2d.fillRect(midX - markerLen/2, y, markerLen, thick); // Top Mid + g2d.fillRect(midX - markerLen/2, y + height - thick, markerLen, thick); // Bottom Mid + g2d.fillRect(x, midY - markerLen/2, thick, markerLen); // Left Mid + g2d.fillRect(x + width - thick, midY - markerLen/2, thick, markerLen); // Right Mid + + g2d.setStroke(oldStroke); + } + + @Override + public void drawDecorativeBorderFilled(final Graphics2D g2d, final int x, final int y, final int width, final int height, final int strength, final Color borderColor, final Color backgroundColor) { + // Fill background + g2d.setColor(backgroundColor); + g2d.fillRect(x, y, width, height); + + // Draw border + this.drawDecorativeBorder(g2d, x, y, width, height, strength, borderColor); + } + + @Override + public void drawWindowPanel(final Graphics2D g2d, final int x, final int y, final int width, final int height, final boolean thickBorder, final Color innerBorderColor, final Color outerBorderColor, final Color backgroundColor, final double scaleFactor) { + // Background + g2d.setColor(backgroundColor); + g2d.fillRect(x, y, width, height); + + // Grid + final Color gridColor = new Color(255, 255, 255, 20); // Faint white grid + g2d.setColor(gridColor); + final int gridSize = (int)(20 * scaleFactor); + if (gridSize > 2) { + for (int gx = x; gx < x + width; gx += gridSize) { + g2d.drawLine(gx, y, gx, y + height); + } + for (int gy = y; gy < y + height; gy += gridSize) { + g2d.drawLine(x, gy, x + width, gy); + } + } + + // Border + final int strength = Math.max((thickBorder ? 3 : 2), (int) ((thickBorder ? 4 : 2) * scaleFactor)); + drawDecorativeBorder(g2d, x, y, width, height, strength, outerBorderColor); + } + + @Override + public void drawCloseButton(final Graphics2D g2d, final int width, final int height, final boolean enabled, final int state, final Color backgroundColor, final double scaleFactor, final Theme theme) { + final var STATE_PRESSED = 2; + final var pressed = (state & STATE_PRESSED) != 0; + + // Button background (transparent or slightly lighter blue) + Color btnBg = backgroundColor; + if (btnBg == null) { + btnBg = pressed ? theme.getSelectionPrimary().toColor() : theme.getBackgroundNormal().toColor().brighter(); + } + g2d.setColor(btnBg); + g2d.fillRect(0, 0, width, height); + + // Border + g2d.setColor(theme.getBorderOuter().toColor()); + g2d.drawRect(0, 0, width - 1, height - 1); + + // X icon + if (enabled) { + g2d.setColor(theme.getTextNormal().toColor()); + + // Draw a technical X (crosshair style?) + // Or just a clean X + final int padding = (int)(4 * scaleFactor); + final int x1 = padding; + final int y1 = padding; + final int x2 = width - padding; + final int y2 = height - padding; + + final Graphics2D g2 = (Graphics2D) g2d.create(); + g2.setStroke(new BasicStroke((float)(2 * scaleFactor))); + g2.drawLine(x1, y1, x2, y2); + g2.drawLine(x1, y2, x2, y1); + g2.dispose(); + } + } +} diff --git a/src/main/java/de/extio/game_engine/renderer/g2d/theme/BlueprintThemeFactoryBean.java b/src/main/java/de/extio/game_engine/renderer/g2d/theme/BlueprintThemeFactoryBean.java new file mode 100644 index 0000000..086da26 --- /dev/null +++ b/src/main/java/de/extio/game_engine/renderer/g2d/theme/BlueprintThemeFactoryBean.java @@ -0,0 +1,46 @@ +package de.extio.game_engine.renderer.g2d.theme; + +import org.springframework.beans.factory.FactoryBean; + +public class BlueprintThemeFactoryBean implements FactoryBean { + + @Override + public Theme getObject() { + return Theme.builder() + .name("Blueprint") + .patternRendererName("blueprintPatternRenderer") + // Highlight/Border (White/Cyan) + .borderOuter(new HSBColor(0.5f, 0.0f, 1.0f)) + // Inner Border (Faint Blue) + .borderInner(new HSBColor(0.6f, 0.5f, 0.8f)) + // Disabled + .borderInnerDisabled(new HSBColor(0.6f, 0.2f, 0.4f)) + // Background (Deep Blueprint Blue) + .backgroundNormal(new HSBColor(0.62f, 0.90f, 0.40f)) + // Selected Background (Lighter Blue) + .backgroundSelected(new HSBColor(0.62f, 0.70f, 0.60f)) + // Text (White) + .textNormal(new HSBColor(0.0f, 0.0f, 1.0f)) + // Text Disabled (Light Blue) + .textDisabled(new HSBColor(0.6f, 0.3f, 0.7f)) + // Selection Primary (Cyan) + .selectionPrimary(new HSBColor(0.5f, 0.8f, 0.9f)) + // Selection Secondary (White) + .selectionSecondary(new HSBColor(0.0f, 0.0f, 1.0f)) + // Window Background (Deep Blueprint Blue) + .windowBackground(new HSBColor(0.62f, 0.90f, 0.40f)) + .hoverBrightnessAdjustment(0.15f) + .pressedBrightnessAdjustment(0.30f) + .build(); + } + + @Override + public Class getObjectType() { + return Theme.class; + } + + @Override + public boolean isSingleton() { + return true; + } +}