feat(renderer): implement z-index management for layered rendering and update component order
This commit is contained in:
@@ -44,7 +44,7 @@ public class Container extends AbstractClientModule implements InitializingBean
|
||||
|
||||
protected final Area2 area = new Area2(MutableCoordI2.create(), MutableCoordI2.create());
|
||||
|
||||
// Normalized area relative to reference resolution, the actual area is aligned based on alignment settings. Scale factors are not considered/handled by the renderer control.
|
||||
// Normalized area of this container relative to reference resolution, the actual position is automatically aligned based on alignment settings (e.g. if the user uses ultra-wide monitors). Global scale factors are not considered/handled by the renderer control.
|
||||
protected final Area2 normalizedArea = new Area2(MutableCoordI2.create(), MutableCoordI2.create());
|
||||
|
||||
protected boolean draggable;
|
||||
@@ -56,6 +56,8 @@ public class Container extends AbstractClientModule implements InitializingBean
|
||||
protected HorizontalAlignment horizontalAlignment = HorizontalAlignment.CENTER;
|
||||
|
||||
protected VerticalAlignment verticalAlignment = VerticalAlignment.CENTER;
|
||||
|
||||
protected short zIndex;
|
||||
|
||||
public Container(final ModuleService moduleService, final RenderingBoPool renderingBoPool, final RendererControl rendererControl, final EventService eventService, final RendererWorkingSet rendererWorkingSet) {
|
||||
this.moduleService = moduleService;
|
||||
@@ -81,6 +83,7 @@ public class Container extends AbstractClientModule implements InitializingBean
|
||||
@Override
|
||||
public void onShow() {
|
||||
DISPLAYED_CONTAINERS.add(this);
|
||||
this.zIndexToTop();
|
||||
this.draw();
|
||||
}
|
||||
|
||||
@@ -161,14 +164,17 @@ public class Container extends AbstractClientModule implements InitializingBean
|
||||
if (this.draggable && event.isDrag()) {
|
||||
Container foregroundContainer = null;
|
||||
synchronized (DISPLAYED_CONTAINERS) {
|
||||
short zIndexHighest = -1;
|
||||
for (final var container : DISPLAYED_CONTAINERS) {
|
||||
if (container.dragging) {
|
||||
foregroundContainer = container;
|
||||
break;
|
||||
}
|
||||
if (SpatialUtils2.intersects(container.area.getPosition(), container.area.getDimension(), event.getScaledCoord(), ImmutableCoordI2.one())) {
|
||||
// TODO z-index handling
|
||||
foregroundContainer = container;
|
||||
if (container.zIndex > zIndexHighest) {
|
||||
zIndexHighest = container.zIndex;
|
||||
foregroundContainer = container;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,7 +212,9 @@ public class Container extends AbstractClientModule implements InitializingBean
|
||||
public void draw() {
|
||||
final var uncommitted = this.rendererWorkingSet.getUncommittedWork(this.id);
|
||||
for (final RenderingBo bo : uncommitted.values()) {
|
||||
bo.withPositionAbsoluteAnchorTopLeft(this.area.getPosition()
|
||||
bo
|
||||
.setZIndex(this.zIndex)
|
||||
.withPositionAbsoluteAnchorTopLeft(this.area.getPosition()
|
||||
.toMutableCoordD2()
|
||||
.add(bo.getLocalX(), bo.getLocalY())
|
||||
.toImmutableCoordI2());
|
||||
@@ -243,6 +251,9 @@ public class Container extends AbstractClientModule implements InitializingBean
|
||||
this.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalized position of this container relative to reference resolution, the actual position is automatically aligned based on alignment settings (e.g. if the user uses ultra-wide monitors). Global scale factors are not considered/handled by the renderer control.
|
||||
*/
|
||||
public CoordI2 getNormalizedPosition() {
|
||||
return this.normalizedArea.getPosition().toImmutableCoordI2();
|
||||
}
|
||||
@@ -252,7 +263,28 @@ public class Container extends AbstractClientModule implements InitializingBean
|
||||
this.area.getDimension().setXY(normalizedDimension);
|
||||
this.draw();
|
||||
}
|
||||
|
||||
private void zIndexToTop() {
|
||||
if (! this.draggable) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (DISPLAYED_CONTAINERS) {
|
||||
for (final var container : DISPLAYED_CONTAINERS) {
|
||||
if (container == this) {
|
||||
continue;
|
||||
}
|
||||
if (container.getzIndex() > this.getzIndex()) {
|
||||
container.setzIndex((short) (container.getzIndex() - 1));
|
||||
}
|
||||
}
|
||||
this.setzIndex((short) (DISPLAYED_CONTAINERS.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalized position of this container relative to reference resolution. Global scale factors are not considered/handled by the renderer control.
|
||||
*/
|
||||
public CoordI2 getNormalizedDimension() {
|
||||
return this.normalizedArea.getDimension().toImmutableCoordI2();
|
||||
}
|
||||
@@ -280,4 +312,13 @@ public class Container extends AbstractClientModule implements InitializingBean
|
||||
public void setVerticalAlignment(final VerticalAlignment verticalAlignment) {
|
||||
this.verticalAlignment = verticalAlignment;
|
||||
}
|
||||
|
||||
public short getzIndex() {
|
||||
return zIndex;
|
||||
}
|
||||
|
||||
public void setzIndex(final short zIndex) {
|
||||
this.zIndex = zIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ public class Window extends Container {
|
||||
.setThickBorder(true)
|
||||
.withDimensionAbsolute(this.area.getDimension())
|
||||
.withPositionAbsoluteAnchorTopLeft(this.area.getPosition())
|
||||
.setZIndex(this.zIndex)
|
||||
.setLayer(RenderingBoLayer.UI_BGR);
|
||||
this.rendererWorkingSet.put(this.id, windowFrame);
|
||||
|
||||
@@ -65,6 +66,7 @@ public class Window extends Container {
|
||||
.setVisible(true)
|
||||
.withDimensionAbsolute(this.area.getDimension())
|
||||
.withPositionAbsoluteAnchorTopLeft(this.area.getPosition())
|
||||
.setZIndex(this.zIndex)
|
||||
.setLayer(RenderingBoLayer.UI1);
|
||||
this.rendererWorkingSet.put(this.id, closeButtonBo);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.extio.game_engine.renderer.g2d;
|
||||
|
||||
import java.awt.AWTEvent;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Graphics;
|
||||
@@ -22,6 +23,7 @@ import java.awt.event.MouseWheelListener;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowListener;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -35,11 +37,12 @@ import de.extio.game_engine.keyboard.KeyModifiers;
|
||||
import de.extio.game_engine.renderer.RendererControl;
|
||||
import de.extio.game_engine.renderer.RendererData;
|
||||
import de.extio.game_engine.renderer.g2d.control.G2DControlHasExclusiveKeyEvent;
|
||||
import de.extio.game_engine.renderer.g2d.control.G2DDrawControl;
|
||||
import de.extio.game_engine.renderer.g2d.control.impl.G2DBaseControlImpl;
|
||||
import de.extio.game_engine.renderer.model.event.KeyStrokeEvent;
|
||||
import de.extio.game_engine.renderer.model.event.MouseClickEvent;
|
||||
import de.extio.game_engine.renderer.model.event.MouseEnterEvent;
|
||||
import de.extio.game_engine.renderer.model.event.MouseExitEvent;
|
||||
import de.extio.game_engine.renderer.model.event.MouseExitEvent;
|
||||
import de.extio.game_engine.renderer.model.event.MouseMoveEvent;
|
||||
import de.extio.game_engine.renderer.model.options.VideoOptions;
|
||||
import de.extio.game_engine.renderer.model.options.VideoOptions.VideoOptionsVideoMode;
|
||||
@@ -474,4 +477,44 @@ public class G2DMainFrame extends Frame {
|
||||
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 {
|
||||
final var componentCount = this.getComponentCount();
|
||||
if (componentCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < componentCount; i++) {
|
||||
final var component = this.getComponent(i);
|
||||
final var componentName = component.getName();
|
||||
if (componentName == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final var control = G2DDrawControl.CACHED_CONTROLS.get(componentName);
|
||||
if (control != null && control instanceof G2DBaseControlImpl) {
|
||||
final var baseControl = (G2DBaseControlImpl) control;
|
||||
COMPONENT_Z_ORDER_PAIRS.add(new ComponentZOrderPair(component, baseControl.getEffectiveLayer()));
|
||||
}
|
||||
}
|
||||
|
||||
COMPONENT_Z_ORDER_PAIRS.sort((a, b) -> Integer.compare(b.effectiveLayer, a.effectiveLayer));
|
||||
|
||||
for (int i = 0; i < COMPONENT_Z_ORDER_PAIRS.size(); i++) {
|
||||
this.setComponentZOrder(COMPONENT_Z_ORDER_PAIRS.get(i).component, i);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
COMPONENT_Z_ORDER_PAIRS.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static record ComponentZOrderPair (Component component, int effectiveLayer) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
|
||||
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(G2DDrawControl.class);
|
||||
|
||||
private static Map<String, BaseControl> CACHED_CONTROLS = new HashMap<>();
|
||||
public static Map<String, BaseControl> CACHED_CONTROLS = new HashMap<>();
|
||||
|
||||
public static Map<String, List<BaseControl>> CONTROL_GROUPS = new HashMap<>();
|
||||
|
||||
@@ -218,6 +218,8 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
|
||||
control.setEnabled(this.enabled);
|
||||
control.setCaption(this.caption);
|
||||
control.setTooltip(this.tooltip);
|
||||
control.setZIndex(this.zIndex);
|
||||
control.setLayer(this.layer);
|
||||
|
||||
if (this.clazz == LabelControl.class) {
|
||||
((LabelControl) control).setForegroundColor(this.color);
|
||||
|
||||
@@ -11,8 +11,10 @@ import org.slf4j.LoggerFactory;
|
||||
import de.extio.game_engine.renderer.RendererData;
|
||||
import de.extio.game_engine.renderer.g2d.G2DRenderer;
|
||||
import de.extio.game_engine.renderer.g2d.G2DRenderingHintFactory;
|
||||
import de.extio.game_engine.renderer.g2d.control.G2DDrawControl;
|
||||
import de.extio.game_engine.renderer.g2d.control.G2DDrawControlTooltip;
|
||||
import de.extio.game_engine.renderer.g2d.control.G2DDrawControlTooltip.TooltipRecord;
|
||||
import de.extio.game_engine.renderer.model.RenderingBoLayer;
|
||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.BaseControl;
|
||||
import de.extio.game_engine.spatial2.SpatialUtils2;
|
||||
import de.extio.game_engine.spatial2.model.CoordI2;
|
||||
@@ -22,6 +24,8 @@ public abstract class G2DBaseControlImpl implements BaseControl {
|
||||
|
||||
protected final static Logger LOGGER = LoggerFactory.getLogger(G2DBaseControlImpl.class);
|
||||
|
||||
private static final short ZINDEX_STEPS = RenderingBoLayer.UI_TOP - RenderingBoLayer.UI_BGR + 1;
|
||||
|
||||
protected int x;
|
||||
|
||||
protected int y;
|
||||
@@ -60,6 +64,10 @@ public abstract class G2DBaseControlImpl implements BaseControl {
|
||||
|
||||
protected CoordI2 tooltipMousePosition;
|
||||
|
||||
protected short zIndex;
|
||||
|
||||
protected short layer;
|
||||
|
||||
@Override
|
||||
public BaseControl setInUse(final boolean inUse) {
|
||||
this.inUse = inUse;
|
||||
@@ -167,6 +175,21 @@ public abstract class G2DBaseControlImpl implements BaseControl {
|
||||
return this;
|
||||
}
|
||||
|
||||
public G2DBaseControlImpl setZIndex(final short zIndex) {
|
||||
this.modified |= this.zIndex != zIndex;
|
||||
this.zIndex = zIndex;
|
||||
return this;
|
||||
}
|
||||
|
||||
public short getZIndex() {
|
||||
return this.zIndex;
|
||||
}
|
||||
|
||||
public G2DBaseControlImpl setLayer(final short layer) {
|
||||
this.layer = layer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
if (this.visible && this.bufferedImage != null) {
|
||||
@@ -224,4 +247,45 @@ public abstract class G2DBaseControlImpl implements BaseControl {
|
||||
}
|
||||
}
|
||||
|
||||
protected int calculateComponentZOrder() {
|
||||
final var mainFrame = ((G2DRenderer) this.rendererData.getRenderer()).getMainFrame();
|
||||
final var componentCount = mainFrame.getComponentCount();
|
||||
|
||||
if (componentCount == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final var myEffectiveLayer = this.getEffectiveLayer();
|
||||
|
||||
int zOrder = 0;
|
||||
for (int i = 0; i < componentCount; i++) {
|
||||
final var component = mainFrame.getComponent(i);
|
||||
final var componentName = component.getName();
|
||||
if (componentName == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final var otherControl = G2DDrawControl.CACHED_CONTROLS.get(componentName);
|
||||
if (otherControl != null && otherControl instanceof final G2DBaseControlImpl baseControl) {
|
||||
final var otherEffectiveLayer = baseControl.getEffectiveLayer();
|
||||
if (otherEffectiveLayer < myEffectiveLayer) {
|
||||
zOrder++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Math.min(zOrder, componentCount - 1);
|
||||
}
|
||||
|
||||
public int getEffectiveLayer() {
|
||||
if (this.layer >= RenderingBoLayer.UI_BGR && this.layer <= RenderingBoLayer.UI_TOP) {
|
||||
return this.layer + (this.zIndex * ZINDEX_STEPS);
|
||||
}
|
||||
return this.layer;
|
||||
}
|
||||
|
||||
protected void updateAllComponentZOrder() {
|
||||
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().recalculateAllComponentZOrder();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ public class G2DButtonControlImpl extends G2DBaseControlImpl implements ButtonCo
|
||||
|
||||
this.createControl();
|
||||
this.initControl();
|
||||
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().add(this.control);
|
||||
final var mainFrame = ((G2DRenderer) this.rendererData.getRenderer()).getMainFrame();
|
||||
mainFrame.add(this.control);
|
||||
this.updateAllComponentZOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,6 +46,7 @@ public class G2DButtonControlImpl extends G2DBaseControlImpl implements ButtonCo
|
||||
this.rebuildBufferedImage();
|
||||
this.control.paint(this.bufferedImageGraphics);
|
||||
this.control.setDirty(false);
|
||||
this.updateAllComponentZOrder();
|
||||
}
|
||||
else if (this.positionModified) {
|
||||
this.control.setLocation(this.x, this.y);
|
||||
|
||||
@@ -56,7 +56,9 @@ public class G2DLabelControlImpl extends G2DBaseControlImpl implements LabelCont
|
||||
|
||||
this.createControl();
|
||||
this.initControl();
|
||||
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().add(this.control);
|
||||
final var mainFrame = ((G2DRenderer) this.rendererData.getRenderer()).getMainFrame();
|
||||
mainFrame.add(this.control);
|
||||
this.updateAllComponentZOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,6 +76,7 @@ public class G2DLabelControlImpl extends G2DBaseControlImpl implements LabelCont
|
||||
this.rebuildBufferedImage();
|
||||
this.control.paint(this.bufferedImageGraphics);
|
||||
this.control.setDirty(false);
|
||||
this.updateAllComponentZOrder();
|
||||
}
|
||||
if (this.tooltip != null) {
|
||||
this.tooltipMousePosition = this.control.getLastMousePosition();
|
||||
|
||||
@@ -80,7 +80,9 @@ public class G2DSliderControlImpl extends G2DBaseControlImpl implements SliderCo
|
||||
|
||||
this.createControl();
|
||||
this.initControl();
|
||||
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().add(this.control);
|
||||
final var mainFrame = ((G2DRenderer) this.rendererData.getRenderer()).getMainFrame();
|
||||
mainFrame.add(this.control);
|
||||
this.updateAllComponentZOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,6 +106,7 @@ public class G2DSliderControlImpl extends G2DBaseControlImpl implements SliderCo
|
||||
this.rebuildBufferedImage();
|
||||
this.control.paint(this.bufferedImageGraphics);
|
||||
this.control.setDirty(false);
|
||||
this.updateAllComponentZOrder();
|
||||
}
|
||||
if (this.tooltip != null) {
|
||||
this.tooltipMousePosition = this.control.getLastMousePosition();
|
||||
|
||||
@@ -58,7 +58,9 @@ public class G2DTableControlImpl extends G2DBaseControlImpl implements TableCont
|
||||
|
||||
this.createControl();
|
||||
this.initControl();
|
||||
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().add(this.control);
|
||||
final var mainFrame = ((G2DRenderer) this.rendererData.getRenderer()).getMainFrame();
|
||||
mainFrame.add(this.control);
|
||||
this.updateAllComponentZOrder();
|
||||
|
||||
this.scrollbar.setColor(RgbaColor.LIGHT_GRAY);
|
||||
this.scrollbar.setHorizontal(false);
|
||||
@@ -78,7 +80,7 @@ public class G2DTableControlImpl extends G2DBaseControlImpl implements TableCont
|
||||
this.rebuildBufferedImage();
|
||||
this.initControl();
|
||||
this.control.setDirty(false);
|
||||
|
||||
this.updateAllComponentZOrder();
|
||||
}
|
||||
this.control.paint(this.bufferedImageGraphics);
|
||||
|
||||
|
||||
@@ -64,7 +64,9 @@ public class G2DTextfieldControlImpl extends G2DBaseControlImpl implements Textf
|
||||
});
|
||||
}
|
||||
|
||||
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().add(this.textComponent);
|
||||
final var mainFrame = ((G2DRenderer) this.rendererData.getRenderer()).getMainFrame();
|
||||
mainFrame.add(this.textComponent);
|
||||
this.updateAllComponentZOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,6 +100,7 @@ public class G2DTextfieldControlImpl extends G2DBaseControlImpl implements Textf
|
||||
else {
|
||||
((CustomTextField) this.textComponent).setDirty(false);
|
||||
}
|
||||
this.updateAllComponentZOrder();
|
||||
}
|
||||
|
||||
super.render();
|
||||
|
||||
@@ -136,9 +136,11 @@ public class G2DTextfieldControlImpl2 extends G2DBaseControlImpl implements Text
|
||||
this.textComponent.setName(this.id);
|
||||
this.initControl();
|
||||
|
||||
final var mainFrame = ((G2DRenderer) this.rendererData.getRenderer()).getMainFrame();
|
||||
if (this.multiLine) {
|
||||
// ((G2DRenderer) this.RendererData.getRenderer()).getMainFrame().add(this.scrollPane);
|
||||
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().add(this.textComponent);
|
||||
// mainFrame.add(this.scrollPane);
|
||||
mainFrame.add(this.textComponent);
|
||||
this.updateAllComponentZOrder();
|
||||
|
||||
this.scrollbar.setColor(RgbaColor.LIGHT_GRAY);
|
||||
this.scrollbar.setHorizontal(false);
|
||||
@@ -147,7 +149,8 @@ public class G2DTextfieldControlImpl2 extends G2DBaseControlImpl implements Text
|
||||
this.scrollbar.build();
|
||||
}
|
||||
else {
|
||||
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().add(this.textComponent);
|
||||
mainFrame.add(this.textComponent);
|
||||
this.updateAllComponentZOrder();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +191,7 @@ public class G2DTextfieldControlImpl2 extends G2DBaseControlImpl implements Text
|
||||
else {
|
||||
((CustomJTextField) this.textComponent).setDirty(false);
|
||||
}
|
||||
this.updateAllComponentZOrder();
|
||||
}
|
||||
|
||||
if (this.multiLine) {
|
||||
|
||||
Reference in New Issue
Block a user