feat(renderer): add CustomWindowPanel and WindowPanelControl for enhanced UI rendering

This commit is contained in:
extio-de
2025-12-27 18:43:32 +01:00
parent 7bc529a473
commit 6919d81c12
6 changed files with 328 additions and 65 deletions

View File

@@ -9,7 +9,7 @@ import de.extio.game_engine.renderer.RendererControl;
import de.extio.game_engine.renderer.model.RenderingBoLayer;
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.WindowCloseButtonControl;
import de.extio.game_engine.renderer.model.bo.DrawWindowRenderingBo;
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.WindowPanelControl;
import de.extio.game_engine.renderer.model.event.UiControlEvent;
import de.extio.game_engine.renderer.work.RendererWorkingSet;
import de.extio.game_engine.renderer.work.RenderingBoPool;
@@ -49,13 +49,16 @@ public class Window extends Container {
return;
}
final var windowFrame = this.rendererWorkingSet.getOrAcquire(this.id, "windowFrame", DrawWindowRenderingBo.class)
.setThickBorder(true)
final var windowPanel = this.renderingBoPool.acquire(this.id + "_WindowPanel", ControlRenderingBo.class)
.setType(WindowPanelControl.class)
.setCustomData(true)
.setEnabled(true)
.setVisible(true)
.withDimensionAbsolute(this.area.getDimension())
.withPositionAbsoluteAnchorTopLeft(this.area.getPosition())
.setZIndex(this.zIndex)
.setLayer(RenderingBoLayer.UI_BGR);
this.rendererWorkingSet.put(this.id, windowFrame);
this.rendererWorkingSet.put(this.id, windowPanel);
if (this.closeButton) {
this.closeButtonName = this.id + "_CloseButton";
@@ -67,7 +70,7 @@ public class Window extends Container {
.withDimensionAbsolute(this.area.getDimension())
.withPositionAbsoluteAnchorTopLeft(this.area.getPosition())
.setZIndex(this.zIndex)
.setLayer(RenderingBoLayer.UI1);
.setLayer(RenderingBoLayer.UI_TOP);
this.rendererWorkingSet.put(this.id, closeButtonBo);
}

View File

@@ -149,9 +149,7 @@ public class G2DMainFrame extends Frame {
@Override
public void mouseReleased(final MouseEvent e) {
try {
final var rawCoord = ImmutableCoordI2.create(e.getX(), e.getY());
final var scaleCoord = G2DMainFrame.this.scaleCoord(rawCoord);
G2DMainFrame.this.rendererData.getEventService().fire(new MouseClickEvent(false, G2DMainFrame.getModifiers(e), e.getButton(), rawCoord, scaleCoord));
G2DMainFrame.this.handleMouseReleased(e, ImmutableCoordI2.zero());
}
catch (final Exception exc) {
LOGGER.warn(exc.getMessage());
@@ -161,10 +159,7 @@ public class G2DMainFrame extends Frame {
@Override
public void mousePressed(final MouseEvent e) {
try {
G2DMainFrame.this.requestFocus();
final var rawCoord = ImmutableCoordI2.create(e.getX(), e.getY());
final var scaleCoord = G2DMainFrame.this.scaleCoord(rawCoord);
G2DMainFrame.this.rendererData.getEventService().fire(new MouseClickEvent(true, G2DMainFrame.getModifiers(e), e.getButton(), rawCoord, scaleCoord));
G2DMainFrame.this.handleMousePressed(e, ImmutableCoordI2.zero());
}
catch (final Exception exc) {
LOGGER.warn(exc.getMessage());
@@ -175,7 +170,7 @@ public class G2DMainFrame extends Frame {
public void mouseExited(final MouseEvent e) {
try {
final var rawCoord = ImmutableCoordI2.create(e.getX(), e.getY());
final var scaleCoord = G2DMainFrame.this.scaleCoord(rawCoord);
final var scaleCoord = G2DMainFrame.this.scaleCoord(rawCoord);
G2DMainFrame.this.rendererData.getEventService().fire(new MouseExitEvent(G2DMainFrame.getModifiers(e), rawCoord, scaleCoord));
}
catch (final Exception exc) {
@@ -187,7 +182,7 @@ public class G2DMainFrame extends Frame {
public void mouseEntered(final MouseEvent e) {
try {
final var rawCoord = ImmutableCoordI2.create(e.getX(), e.getY());
final var scaleCoord = G2DMainFrame.this.scaleCoord(rawCoord);
final var scaleCoord = G2DMainFrame.this.scaleCoord(rawCoord);
G2DMainFrame.this.rendererData.getEventService().fire(new MouseEnterEvent(G2DMainFrame.getModifiers(e), rawCoord, scaleCoord));
}
catch (final Exception exc) {
@@ -205,66 +200,23 @@ public class G2DMainFrame extends Frame {
@Override
public void mouseMoved(final MouseEvent e) {
final var button = this.getButton(e);
try {
final var rawCoord = ImmutableCoordI2.create(e.getX(), e.getY());
final var scaleCoord = G2DMainFrame.this.scaleCoord(rawCoord);
G2DMainFrame.this.rendererData.getEventService().fire(new MouseMoveEvent(false, G2DMainFrame.getModifiers(e), button, rawCoord, scaleCoord));
}
catch (final Exception exc) {
if ("Engine not started up yet".equals(exc.getMessage())) {
return;
}
LOGGER.error(exc.getMessage(), exc);
}
G2DMainFrame.this.handleMouseMoved(e, ImmutableCoordI2.zero());
}
@Override
public void mouseDragged(final MouseEvent e) {
final var button = this.getButton(e);
try {
final var rawCoord = ImmutableCoordI2.create(e.getX(), e.getY());
final var scaleCoord = G2DMainFrame.this.scaleCoord(rawCoord);
G2DMainFrame.this.rendererData.getEventService().fire(new MouseMoveEvent(true, G2DMainFrame.getModifiers(e), button, rawCoord, scaleCoord));
}
catch (final Exception exc) {
if ("Engine not started up yet".equals(exc.getMessage())) {
return;
}
LOGGER.error(exc.getMessage(), exc);
}
G2DMainFrame.this.handleMouseDragged(e, ImmutableCoordI2.zero());
}
private int getButton(final MouseEvent e) {
var button = MouseEvent.NOBUTTON;
if ((e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0) {
button = MouseEvent.BUTTON1;
}
else if ((e.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK) != 0) {
button = MouseEvent.BUTTON2;
}
else if ((e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0) {
button = MouseEvent.BUTTON3;
}
return button;
}
});
this.addMouseWheelListener(new MouseWheelListener() {
@Override
public void mouseWheelMoved(final MouseWheelEvent e) {
try {
final var rawCoord = ImmutableCoordI2.create(e.getX(), e.getY());
final var scaleCoord = G2DMainFrame.this.scaleCoord(rawCoord);
G2DMainFrame.this.rendererData.getEventService().fire(new MouseClickEvent(true, G2DMainFrame.getModifiers(e), e.getWheelRotation() == -1 ? 4 : 5, rawCoord, scaleCoord));
}
catch (final Exception exc) {
LOGGER.warn(exc.getMessage());
}
G2DMainFrame.this.handleWheelMoved(e, ImmutableCoordI2.zero());
}
});
this.aWTEventListener = new AWTEventListener() {
@@ -454,6 +406,70 @@ public class G2DMainFrame extends Frame {
this.setSize((int) Math.min(bounds.getWidth(), RendererControl.REFERENCE_RESOLUTION.getX()), (int) Math.min(bounds.getHeight(), RendererControl.REFERENCE_RESOLUTION.getY()));
}
public void handleMouseReleased(final MouseEvent e, final CoordI2 offset) {
final var rawCoord = ImmutableCoordI2.create(e.getX() + offset.getX(), e.getY() + offset.getY());
final var scaleCoord = this.scaleCoord(rawCoord);
this.rendererData.getEventService().fire(new MouseClickEvent(false, G2DMainFrame.getModifiers(e), e.getButton(), rawCoord, scaleCoord));
}
public void handleMousePressed(final MouseEvent e, final CoordI2 offset) {
this.requestFocus();
final var rawCoord = ImmutableCoordI2.create(e.getX() + offset.getX(), e.getY() + offset.getY());
final var scaleCoord = this.scaleCoord(rawCoord);
this.rendererData.getEventService().fire(new MouseClickEvent(true, G2DMainFrame.getModifiers(e), e.getButton(), rawCoord, scaleCoord));
}
public void handleMouseMoved(final MouseEvent e, final CoordI2 offset) {
final var button = getMouseButton(e);
try {
final var rawCoord = ImmutableCoordI2.create(e.getX() + offset.getX(), e.getY() + offset.getY());
final var scaleCoord = this.scaleCoord(rawCoord);
this.rendererData.getEventService().fire(new MouseMoveEvent(false, G2DMainFrame.getModifiers(e), button, rawCoord, scaleCoord));
}
catch (final Exception exc) {
LOGGER.warn(exc.getMessage(), exc);
}
}
public void handleMouseDragged(final MouseEvent e, final CoordI2 offset) {
final var button = getMouseButton(e);
try {
final var rawCoord = ImmutableCoordI2.create(e.getX() + offset.getX(), e.getY() + offset.getY());
final var scaleCoord = this.scaleCoord(rawCoord);
this.rendererData.getEventService().fire(new MouseMoveEvent(true, G2DMainFrame.getModifiers(e), button, rawCoord, scaleCoord));
}
catch (final Exception exc) {
LOGGER.warn(exc.getMessage(), exc);
}
}
public void handleWheelMoved(final MouseWheelEvent e, final CoordI2 offset) {
try {
final var rawCoord = ImmutableCoordI2.create(e.getX() + offset.getX(), e.getY() + offset.getY());
final var scaleCoord = this.scaleCoord(rawCoord);
this.rendererData.getEventService().fire(new MouseClickEvent(true, G2DMainFrame.getModifiers(e), e.getWheelRotation() == -1 ? 4 : 5, rawCoord, scaleCoord));
}
catch (final Exception exc) {
LOGGER.warn(exc.getMessage());
}
}
private static int getMouseButton(final MouseEvent e) {
var button = MouseEvent.NOBUTTON;
if ((e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0) {
button = MouseEvent.BUTTON1;
}
else if ((e.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK) != 0) {
button = MouseEvent.BUTTON2;
}
else if ((e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0) {
button = MouseEvent.BUTTON3;
}
return button;
}
private static int getModifiers(final InputEvent event) {
var result = 0;
@@ -472,14 +488,14 @@ public class G2DMainFrame extends Frame {
return result;
}
private CoordI2 scaleCoord(final CoordI2 rawCoord) {
final var scaleFactor = ((G2DRendererControl) this.rendererData.getRendererControl()).getScaleFactor();
return ImmutableCoordI2.create((int) (rawCoord.getX() / scaleFactor), (int) (rawCoord.getY() / scaleFactor));
}
private static final List<ComponentZOrderPair> COMPONENT_Z_ORDER_PAIRS = new ArrayList<>();
public void recalculateAllComponentZOrder() {
synchronized (getTreeLock()) {
try {
@@ -514,7 +530,7 @@ public class G2DMainFrame extends Frame {
}
}
private static record ComponentZOrderPair (Component component, int effectiveLayer) {
private static record ComponentZOrderPair(Component component, int effectiveLayer) {
}
}

View File

@@ -24,6 +24,7 @@ import de.extio.game_engine.renderer.g2d.control.impl.G2DTextfieldControlImpl2;
import de.extio.game_engine.renderer.g2d.control.impl.G2DToggleButtonControlImpl;
import de.extio.game_engine.renderer.g2d.control.impl.G2DTooltipControl;
import de.extio.game_engine.renderer.g2d.control.impl.G2DWindowCloseButtonControlImpl;
import de.extio.game_engine.renderer.g2d.control.impl.G2DWindowPanelControlImpl;
import de.extio.game_engine.renderer.model.RenderingBo;
import de.extio.game_engine.renderer.model.RenderingBoLayer;
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
@@ -156,6 +157,9 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
else if (this.clazz == WindowCloseButtonControl.class) {
control = new G2DWindowCloseButtonControlImpl();
}
else if (this.clazz == WindowPanelControl.class) {
control = new G2DWindowPanelControlImpl();
}
else if (this.clazz == SliderControl.class) {
control = new G2DSliderControlImpl();
}
@@ -263,6 +267,12 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
((WindowCloseButtonControl) control).setThickBorder(((Boolean) this.customData).booleanValue());
}
}
else if (this.clazz == WindowPanelControl.class) {
((WindowPanelControl) control).setColor(this.color);
if (this.customData instanceof Boolean) {
((WindowPanelControl) control).setThickBorder(((Boolean) this.customData).booleanValue());
}
}
else if (this.clazz == SliderControl.class) {
((SliderControl) control).setColor(this.color);
if (this.customData instanceof Boolean) {

View File

@@ -0,0 +1,126 @@
package de.extio.game_engine.renderer.g2d.control.components;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import de.extio.game_engine.renderer.g2d.G2DMainFrame;
import de.extio.game_engine.renderer.model.color.ImmutableRgbaColor;
import de.extio.game_engine.renderer.model.color.RgbaColor;
import de.extio.game_engine.spatial2.model.ImmutableCoordI2;
public class CustomWindowPanel extends Component {
private static final RgbaColor COLOR_WINDOW = new ImmutableRgbaColor(ComponentRenderingSupport.COLOR_COMPONENT_BORDER1.getRed(), ComponentRenderingSupport.COLOR_COMPONENT_BORDER1.getGreen(), ComponentRenderingSupport.COLOR_COMPONENT_BORDER1.getBlue());
protected boolean thickBorder;
protected Color color;
protected double scaleFactor;
protected boolean dirty = true;
public void setThickBorder(final boolean thickBorder) {
this.thickBorder = thickBorder;
}
public void setColor(final Color color) {
this.color = color;
}
public void setScaleFactor(final double scaleFactor) {
this.scaleFactor = scaleFactor;
}
public boolean isDirty() {
return this.dirty;
}
public void setDirty(final boolean dirty) {
this.dirty = dirty;
}
public CustomWindowPanel() {
this.setIgnoreRepaint(true);
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(final MouseEvent e) {
if (CustomWindowPanel.this.getParent() instanceof final G2DMainFrame mainFrame) {
mainFrame.handleMouseReleased(e, ImmutableCoordI2.create(CustomWindowPanel.this.getX(), CustomWindowPanel.this.getY()));
}
}
@Override
public void mousePressed(final MouseEvent e) {
if (CustomWindowPanel.this.getParent() instanceof final G2DMainFrame mainFrame) {
mainFrame.handleMousePressed(e, ImmutableCoordI2.create(CustomWindowPanel.this.getX(), CustomWindowPanel.this.getY()));
}
}
});
this.addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseDragged(final MouseEvent e) {
if (CustomWindowPanel.this.getParent() instanceof final G2DMainFrame mainFrame) {
mainFrame.handleMouseDragged(e, ImmutableCoordI2.create(CustomWindowPanel.this.getX(), CustomWindowPanel.this.getY()));
}
}
@Override
public void mouseMoved(final MouseEvent e) {
if (CustomWindowPanel.this.getParent() instanceof final G2DMainFrame mainFrame) {
mainFrame.handleMouseMoved(e, ImmutableCoordI2.create(CustomWindowPanel.this.getX(), CustomWindowPanel.this.getY()));
}
}
});
this.addMouseWheelListener(new MouseWheelListener() {
@Override
public void mouseWheelMoved(final MouseWheelEvent e) {
if (CustomWindowPanel.this.getParent() instanceof final G2DMainFrame mainFrame) {
mainFrame.handleWheelMoved(e, ImmutableCoordI2.create(CustomWindowPanel.this.getX(), CustomWindowPanel.this.getY()));
}
}
});
}
@Override
public void update(final Graphics g) {
//
}
@Override
public void paint(final Graphics g) {
final var g2d = (Graphics2D) g;
final var strength = Math.max(2, (int) (3 * this.scaleFactor));
final var effectiveColor = this.color != null ? this.color : COLOR_WINDOW.toAwtColor();
final var cMain = effectiveColor;
final var rgbaDarker = new Color(effectiveColor.getRed() / 2, effectiveColor.getGreen() / 2, effectiveColor.getBlue() / 2);
if (this.thickBorder) {
final var borderStrength = strength * 2;
g2d.setColor(cMain);
g2d.fillRect(strength * 2, strength * 2, this.getWidth() - strength * 4, borderStrength);
g2d.setColor(rgbaDarker);
g2d.fillRect(strength * 2, strength * 2 + borderStrength, this.getWidth() - strength * 4, strength);
}
ComponentRenderingSupport.drawDecorativeBorder(g2d, 0, 0, this.getWidth(), this.getHeight(), strength, rgbaDarker);
ComponentRenderingSupport.drawDecorativeBorderFilled(g2d, strength, strength, this.getWidth() - strength * 2, this.getHeight() - strength * 2, strength, cMain);
}
}

View File

@@ -0,0 +1,93 @@
package de.extio.game_engine.renderer.g2d.control.impl;
import de.extio.game_engine.renderer.g2d.G2DRenderer;
import de.extio.game_engine.renderer.g2d.control.components.CustomWindowPanel;
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.WindowPanelControl;
import de.extio.game_engine.renderer.model.color.RgbaColor;
public class G2DWindowPanelControlImpl extends G2DBaseControlImpl implements WindowPanelControl {
protected CustomWindowPanel control;
protected boolean thickBorder;
protected RgbaColor color;
@Override
public boolean isThickBorder() {
return this.thickBorder;
}
@Override
public void setThickBorder(final boolean thickBorder) {
this.modified |= thickBorder != this.thickBorder;
this.thickBorder = thickBorder;
}
@Override
public RgbaColor getColor() {
return this.color;
}
@Override
public void setColor(final RgbaColor color) {
this.modified |= (color == null) ? this.color != null : !color.equals(this.color);
this.color = color;
}
@Override
public void build() {
super.build();
this.createControl();
this.initControl();
final var mainFrame = ((G2DRenderer) this.rendererData.getRenderer()).getMainFrame();
mainFrame.add(this.control);
this.updateAllComponentZOrder();
}
@Override
public void performAction() {
// Window panel is decorative, no action needed
}
@Override
public void render() {
if (this.modified || this.control.isDirty()) {
this.initControl();
this.rebuildBufferedImage();
this.control.paint(this.bufferedImageGraphics);
this.control.setDirty(false);
this.updateAllComponentZOrder();
}
super.render();
}
@Override
public void close() {
this.control.setVisible(false);
this.control.invalidate();
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().remove(this.control);
this.thickBorder = false;
this.color = null;
super.close();
}
protected void createControl() {
this.control = new CustomWindowPanel();
this.control.setName(this.id);
}
protected void initControl() {
this.control.setLocation(this.x, this.y);
this.control.setSize(this.width, this.height);
this.control.setScaleFactor(this.scaleFactor);
this.control.setThickBorder(this.thickBorder);
this.control.setColor(this.color != null ? this.color.toAwtColor() : null);
this.control.setVisible(this.visible);
}
}

View File

@@ -165,6 +165,21 @@ public interface ControlRenderingBo extends RenderingBoHasDimension {
}
/**
* Window panel (decorative container)
*/
public static interface WindowPanelControl extends BaseControl {
boolean isThickBorder();
void setThickBorder(boolean thickBorder);
RgbaColor getColor();
void setColor(RgbaColor color);
}
/**
* Text field
*/