feat(renderer): implement z-index management for layered rendering and update component order

This commit is contained in:
extio-de
2025-12-27 16:16:05 +01:00
parent f36492197f
commit 7bc529a473
12 changed files with 354 additions and 15 deletions

169
README.md
View File

@@ -263,12 +263,181 @@ The `RendererWorkingSet` supports two rendering paradigms:
Both approaches can be mixed within the same application, allowing each module to choose the most appropriate strategy for its needs. Both approaches can be mixed within the same application, allowing each module to choose the most appropriate strategy for its needs.
#### UI Controls
The renderer provides a comprehensive set of interactive UI controls that can be used to build game interfaces. Controls are created as `ControlRenderingBo` objects and support common UI elements with automatic event handling.
**Available Control Types:**
- **`ButtonControl`**: Standard clickable button
- **`ToggleButtonControl`**: Button with on/off state
- **`SwitchControl`**: Toggle switch with visual state
- **`LabelControl`**: Non-interactive text display with optional background
- **`TextfieldControl`**: Single-line or multi-line text input
- **`SliderControl`**: Value slider with horizontal/vertical orientation
- **`TableControl`**: Data table with scrolling support
- **`WindowCloseButtonControl`**: Specialized close button for windows
- **`TooltipControl`**: Hover tooltip display
**Creating and Using Controls:**
```java
// Create a button control
var button = renderingBoPool.acquire("myButton", ControlRenderingBo.class)
.setType(ButtonControl.class)
.setCaption("Click Me")
.setFontSize(16)
.setVisible(true)
.setEnabled(true)
.withPositionAbsoluteAnchorTopLeft(100, 50)
.withDimensionAbsolute(120, 40);
// Add to working set
rendererWorkingSet.put(moduleId, button);
// Create a toggle button with custom state
var toggle = renderingBoPool.acquire("myToggle", ControlRenderingBo.class)
.setType(ToggleButtonControl.class)
.setCaption("Toggle")
.setCustomData(Boolean.TRUE) // Initial toggled state
.withPositionAbsoluteAnchorTopLeft(250, 50)
.withDimensionAbsolute(100, 40);
// Create a label with custom colors
var label = renderingBoPool.acquire("myLabel", ControlRenderingBo.class)
.setType(LabelControl.class)
.setCaption("Status: Ready")
.setColor(RgbaColor.WHITE) // Foreground color
.setCustomData(RgbaColor.DARK_GRAY) // Background color
.setCustomData2(HorizontalAlignment.CENTER) // Text alignment
.withPositionAbsoluteAnchorTopLeft(100, 100)
.withDimensionAbsolute(200, 30);
// Create a text field (multi-line)
var textField = renderingBoPool.acquire("myTextField", ControlRenderingBo.class)
.setType(TextfieldControl.class)
.setCaption("Enter text here...")
.setCustomData(Boolean.TRUE) // Multi-line mode
.setCustomData2(Boolean.FALSE) // Not read-only
.withPositionAbsoluteAnchorTopLeft(100, 150)
.withDimensionAbsolute(300, 100);
```
**Handling Control Events:**
Controls fire `UiControlEvent` instances when interacted with. Register event handlers to respond to user actions:
```java
// In your module's onActivate() method
eventService.registerConsumer(
moduleId,
UiControlEvent.class,
event -> {
var controlId = event.getControlId();
var eventData = event.getData();
switch (controlId) {
case "myButton" -> handleButtonClick();
case "myToggle" -> handleToggleChange((Boolean) eventData);
case "myTextField" -> handleTextChange((String) eventData);
}
}
);
```
**Control Groups:**
Controls can be organized into groups for coordinated behavior (e.g., radio button groups where only one can be selected):
```java
var option1 = renderingBoPool.acquire("option1", ControlRenderingBo.class)
.setType(ToggleButtonControl.class)
.setControlGroup("radioGroup")
.setCaption("Option 1");
var option2 = renderingBoPool.acquire("option2", ControlRenderingBo.class)
.setType(ToggleButtonControl.class)
.setControlGroup("radioGroup")
.setCaption("Option 2");
```
When toggle buttons share a control group, only one can be active at a time - selecting one automatically deselects the others.
**Custom Data Fields:**
Controls support up to four custom data fields (`setCustomData()` through `setCustomData4()`) for control-specific configuration:
- Toggle buttons: `customData` = initial state (Boolean)
- Labels: `customData` = background color, `customData2` = text alignment
- Text fields: `customData` = multi-line mode (Boolean), `customData2` = read-only flag
- Sliders: `customData` = horizontal orientation, `customData2` = current value, `customData3` = secondary value
- Tables: `customData` = data list, `customData2` = row count, `customData3` = version number
#### Integration with Module System #### Integration with Module System
The Renderer System integrates with the Module System to provide automatic cleanup of rendering objects. When a client module (`AbstractClientModule`) is deactivated or unloaded, its entire renderer working set is automatically cleared by the Module Service. This ensures that rendering objects associated with inactive or unloaded modules are not rendered and don't consume memory. The Renderer System integrates with the Module System to provide automatic cleanup of rendering objects. When a client module (`AbstractClientModule`) is deactivated or unloaded, its entire renderer working set is automatically cleared by the Module Service. This ensures that rendering objects associated with inactive or unloaded modules are not rendered and don't consume memory.
Modules don't need to manually clear their rendering objects in their `onDeactivate()` or `onUnload()` lifecycle methods - the Module Service handles this cleanup automatically using `rendererWorkingSet.clear(moduleId)`. Modules don't need to manually clear their rendering objects in their `onDeactivate()` or `onUnload()` lifecycle methods - the Module Service handles this cleanup automatically using `rendererWorkingSet.clear(moduleId)`.
#### Rendering Layers and Z-Index
The rendering system uses a two-level ordering system to control the draw order of rendering objects and UI components:
**Rendering Layers**
All `RenderingBo` objects have a `layer` property that determines their primary rendering order. Objects are rendered from lowest layer value to highest layer value, with higher layers painted on top. The `RenderingBoLayer` class defines standard layer constants:
- **Background Layers**: `BACKGROUND0` (100), `BACKGROUND1` (110), `BACKGROUND2` (120)
- **Foreground Layers**: `FOREGROUND0` (1000) through `FOREGROUND6` (1600)
- **UI Layers**: `UI_BGR` (2000), `UI0` (2100), `UI1` (2200), `UI2` (2300), `UI_TOP` (2499)
- **Top Layer**: `TOP` (32000) - always rendered last
**Z-Index for Fine-Grained Ordering**
Within the UI layers (`UI_BGR` through `UI_TOP`), rendering objects can additionally specify a `zIndex` property for fine-grained ordering. The z-index allows multiple objects on the same layer to be ordered relative to each other:
```java
// Both labels are on UI0 layer, but label2 renders on top due to higher zIndex
var label1 = pool.acquire("label1", LabelBo.class)
.setLayer(RenderingBoLayer.UI0)
.setZIndex((short) 0)
.setText("Behind");
var label2 = pool.acquire("label2", LabelBo.class)
.setLayer(RenderingBoLayer.UI0)
.setZIndex((short) 10)
.setText("In Front");
```
**Effective Layer Calculation**
For objects in UI layers, the effective rendering order is calculated as:
```
effectiveLayer = layer + (zIndex * ZINDEX_STEPS)
where ZINDEX_STEPS = UI_TOP - UI_BGR + 1 = 500
```
This formula ensures that:
- Objects with the same layer and zIndex render in the order they were added
- Higher zIndex values always render on top of lower values within the same layer
- **The zIndex must be less than 48** to prevent overlapping with different layer ranges
**AWT Component Z-Order**
For UI controls implemented as AWT/Swing components (buttons, labels, text fields, etc.), the z-index is synchronized with AWT's component z-order. When a control's zIndex or layer changes, the `G2DMainFrame.recalculateAllComponentZOrder()` method:
1. Collects all components and their effective layers
2. Sorts components by effective layer (ascending order)
3. Applies the sorted z-order to all components using `Container.setComponentZOrder()`
This ensures that AWT components respect the same layering rules as rendered objects, maintaining consistent visual stacking throughout the UI.
**Best Practices**
- Use layers for major UI groupings (backgrounds, game content, UI overlays)
- Use zIndex for ordering within a specific layer (window stacking, overlapping controls)
- Reserve `TOP` layer for critical always-on-top elements (tooltips, modal dialogs)
- **Keep zIndex values below 48** - higher values will cause incorrect rendering order
- For most use cases, zIndex values in the 0-20 range are sufficient
#### Setup / Autoconfiguration #### Setup / Autoconfiguration
**Configuration Class**: `RendererAutoConfiguration` **Configuration Class**: `RendererAutoConfiguration`

View File

@@ -44,7 +44,7 @@ public class Container extends AbstractClientModule implements InitializingBean
protected final Area2 area = new Area2(MutableCoordI2.create(), MutableCoordI2.create()); 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 final Area2 normalizedArea = new Area2(MutableCoordI2.create(), MutableCoordI2.create());
protected boolean draggable; protected boolean draggable;
@@ -57,6 +57,8 @@ public class Container extends AbstractClientModule implements InitializingBean
protected VerticalAlignment verticalAlignment = VerticalAlignment.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) { public Container(final ModuleService moduleService, final RenderingBoPool renderingBoPool, final RendererControl rendererControl, final EventService eventService, final RendererWorkingSet rendererWorkingSet) {
this.moduleService = moduleService; this.moduleService = moduleService;
this.renderingBoPool = renderingBoPool; this.renderingBoPool = renderingBoPool;
@@ -81,6 +83,7 @@ public class Container extends AbstractClientModule implements InitializingBean
@Override @Override
public void onShow() { public void onShow() {
DISPLAYED_CONTAINERS.add(this); DISPLAYED_CONTAINERS.add(this);
this.zIndexToTop();
this.draw(); this.draw();
} }
@@ -161,17 +164,20 @@ public class Container extends AbstractClientModule implements InitializingBean
if (this.draggable && event.isDrag()) { if (this.draggable && event.isDrag()) {
Container foregroundContainer = null; Container foregroundContainer = null;
synchronized (DISPLAYED_CONTAINERS) { synchronized (DISPLAYED_CONTAINERS) {
short zIndexHighest = -1;
for (final var container : DISPLAYED_CONTAINERS) { for (final var container : DISPLAYED_CONTAINERS) {
if (container.dragging) { if (container.dragging) {
foregroundContainer = container; foregroundContainer = container;
break; break;
} }
if (SpatialUtils2.intersects(container.area.getPosition(), container.area.getDimension(), event.getScaledCoord(), ImmutableCoordI2.one())) { if (SpatialUtils2.intersects(container.area.getPosition(), container.area.getDimension(), event.getScaledCoord(), ImmutableCoordI2.one())) {
// TODO z-index handling if (container.zIndex > zIndexHighest) {
zIndexHighest = container.zIndex;
foregroundContainer = container; foregroundContainer = container;
} }
} }
} }
}
if (foregroundContainer == this) { if (foregroundContainer == this) {
if (!this.dragging) { if (!this.dragging) {
this.dragging = true; this.dragging = true;
@@ -206,7 +212,9 @@ public class Container extends AbstractClientModule implements InitializingBean
public void draw() { public void draw() {
final var uncommitted = this.rendererWorkingSet.getUncommittedWork(this.id); final var uncommitted = this.rendererWorkingSet.getUncommittedWork(this.id);
for (final RenderingBo bo : uncommitted.values()) { for (final RenderingBo bo : uncommitted.values()) {
bo.withPositionAbsoluteAnchorTopLeft(this.area.getPosition() bo
.setZIndex(this.zIndex)
.withPositionAbsoluteAnchorTopLeft(this.area.getPosition()
.toMutableCoordD2() .toMutableCoordD2()
.add(bo.getLocalX(), bo.getLocalY()) .add(bo.getLocalX(), bo.getLocalY())
.toImmutableCoordI2()); .toImmutableCoordI2());
@@ -243,6 +251,9 @@ public class Container extends AbstractClientModule implements InitializingBean
this.draw(); 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() { public CoordI2 getNormalizedPosition() {
return this.normalizedArea.getPosition().toImmutableCoordI2(); return this.normalizedArea.getPosition().toImmutableCoordI2();
} }
@@ -253,6 +264,27 @@ public class Container extends AbstractClientModule implements InitializingBean
this.draw(); 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() { public CoordI2 getNormalizedDimension() {
return this.normalizedArea.getDimension().toImmutableCoordI2(); return this.normalizedArea.getDimension().toImmutableCoordI2();
} }
@@ -280,4 +312,13 @@ public class Container extends AbstractClientModule implements InitializingBean
public void setVerticalAlignment(final VerticalAlignment verticalAlignment) { public void setVerticalAlignment(final VerticalAlignment verticalAlignment) {
this.verticalAlignment = verticalAlignment; this.verticalAlignment = verticalAlignment;
} }
public short getzIndex() {
return zIndex;
}
public void setzIndex(final short zIndex) {
this.zIndex = zIndex;
}
} }

View File

@@ -53,6 +53,7 @@ public class Window extends Container {
.setThickBorder(true) .setThickBorder(true)
.withDimensionAbsolute(this.area.getDimension()) .withDimensionAbsolute(this.area.getDimension())
.withPositionAbsoluteAnchorTopLeft(this.area.getPosition()) .withPositionAbsoluteAnchorTopLeft(this.area.getPosition())
.setZIndex(this.zIndex)
.setLayer(RenderingBoLayer.UI_BGR); .setLayer(RenderingBoLayer.UI_BGR);
this.rendererWorkingSet.put(this.id, windowFrame); this.rendererWorkingSet.put(this.id, windowFrame);
@@ -65,6 +66,7 @@ public class Window extends Container {
.setVisible(true) .setVisible(true)
.withDimensionAbsolute(this.area.getDimension()) .withDimensionAbsolute(this.area.getDimension())
.withPositionAbsoluteAnchorTopLeft(this.area.getPosition()) .withPositionAbsoluteAnchorTopLeft(this.area.getPosition())
.setZIndex(this.zIndex)
.setLayer(RenderingBoLayer.UI1); .setLayer(RenderingBoLayer.UI1);
this.rendererWorkingSet.put(this.id, closeButtonBo); this.rendererWorkingSet.put(this.id, closeButtonBo);
} }

View File

@@ -2,6 +2,7 @@ package de.extio.game_engine.renderer.g2d;
import java.awt.AWTEvent; import java.awt.AWTEvent;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.Frame; import java.awt.Frame;
import java.awt.Graphics; import java.awt.Graphics;
@@ -22,6 +23,7 @@ import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.awt.event.WindowListener; import java.awt.event.WindowListener;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; 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.RendererControl;
import de.extio.game_engine.renderer.RendererData; import de.extio.game_engine.renderer.RendererData;
import de.extio.game_engine.renderer.g2d.control.G2DControlHasExclusiveKeyEvent; 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.KeyStrokeEvent;
import de.extio.game_engine.renderer.model.event.MouseClickEvent; 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.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.MouseExitEvent;
import de.extio.game_engine.renderer.model.event.MouseMoveEvent; 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;
import de.extio.game_engine.renderer.model.options.VideoOptions.VideoOptionsVideoMode; 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(); final var scaleFactor = ((G2DRendererControl) this.rendererData.getRendererControl()).getScaleFactor();
return ImmutableCoordI2.create((int) (rawCoord.getX() / scaleFactor), (int) (rawCoord.getY() / scaleFactor)); 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) {
}
} }

View File

@@ -37,7 +37,7 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
private final static Logger LOGGER = LoggerFactory.getLogger(G2DDrawControl.class); 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<>(); 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.setEnabled(this.enabled);
control.setCaption(this.caption); control.setCaption(this.caption);
control.setTooltip(this.tooltip); control.setTooltip(this.tooltip);
control.setZIndex(this.zIndex);
control.setLayer(this.layer);
if (this.clazz == LabelControl.class) { if (this.clazz == LabelControl.class) {
((LabelControl) control).setForegroundColor(this.color); ((LabelControl) control).setForegroundColor(this.color);

View File

@@ -11,8 +11,10 @@ import org.slf4j.LoggerFactory;
import de.extio.game_engine.renderer.RendererData; import de.extio.game_engine.renderer.RendererData;
import de.extio.game_engine.renderer.g2d.G2DRenderer; import de.extio.game_engine.renderer.g2d.G2DRenderer;
import de.extio.game_engine.renderer.g2d.G2DRenderingHintFactory; 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;
import de.extio.game_engine.renderer.g2d.control.G2DDrawControlTooltip.TooltipRecord; 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.renderer.model.bo.ControlRenderingBo.BaseControl;
import de.extio.game_engine.spatial2.SpatialUtils2; import de.extio.game_engine.spatial2.SpatialUtils2;
import de.extio.game_engine.spatial2.model.CoordI2; 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); 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 x;
protected int y; protected int y;
@@ -60,6 +64,10 @@ public abstract class G2DBaseControlImpl implements BaseControl {
protected CoordI2 tooltipMousePosition; protected CoordI2 tooltipMousePosition;
protected short zIndex;
protected short layer;
@Override @Override
public BaseControl setInUse(final boolean inUse) { public BaseControl setInUse(final boolean inUse) {
this.inUse = inUse; this.inUse = inUse;
@@ -167,6 +175,21 @@ public abstract class G2DBaseControlImpl implements BaseControl {
return this; 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 @Override
public void render() { public void render() {
if (this.visible && this.bufferedImage != null) { 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();
}
} }

View File

@@ -27,7 +27,9 @@ public class G2DButtonControlImpl extends G2DBaseControlImpl implements ButtonCo
this.createControl(); this.createControl();
this.initControl(); 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 @Override
@@ -44,6 +46,7 @@ public class G2DButtonControlImpl extends G2DBaseControlImpl implements ButtonCo
this.rebuildBufferedImage(); this.rebuildBufferedImage();
this.control.paint(this.bufferedImageGraphics); this.control.paint(this.bufferedImageGraphics);
this.control.setDirty(false); this.control.setDirty(false);
this.updateAllComponentZOrder();
} }
else if (this.positionModified) { else if (this.positionModified) {
this.control.setLocation(this.x, this.y); this.control.setLocation(this.x, this.y);

View File

@@ -56,7 +56,9 @@ public class G2DLabelControlImpl extends G2DBaseControlImpl implements LabelCont
this.createControl(); this.createControl();
this.initControl(); 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 @Override
@@ -74,6 +76,7 @@ public class G2DLabelControlImpl extends G2DBaseControlImpl implements LabelCont
this.rebuildBufferedImage(); this.rebuildBufferedImage();
this.control.paint(this.bufferedImageGraphics); this.control.paint(this.bufferedImageGraphics);
this.control.setDirty(false); this.control.setDirty(false);
this.updateAllComponentZOrder();
} }
if (this.tooltip != null) { if (this.tooltip != null) {
this.tooltipMousePosition = this.control.getLastMousePosition(); this.tooltipMousePosition = this.control.getLastMousePosition();

View File

@@ -80,7 +80,9 @@ public class G2DSliderControlImpl extends G2DBaseControlImpl implements SliderCo
this.createControl(); this.createControl();
this.initControl(); 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 @Override
@@ -104,6 +106,7 @@ public class G2DSliderControlImpl extends G2DBaseControlImpl implements SliderCo
this.rebuildBufferedImage(); this.rebuildBufferedImage();
this.control.paint(this.bufferedImageGraphics); this.control.paint(this.bufferedImageGraphics);
this.control.setDirty(false); this.control.setDirty(false);
this.updateAllComponentZOrder();
} }
if (this.tooltip != null) { if (this.tooltip != null) {
this.tooltipMousePosition = this.control.getLastMousePosition(); this.tooltipMousePosition = this.control.getLastMousePosition();

View File

@@ -58,7 +58,9 @@ public class G2DTableControlImpl extends G2DBaseControlImpl implements TableCont
this.createControl(); this.createControl();
this.initControl(); 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.setColor(RgbaColor.LIGHT_GRAY);
this.scrollbar.setHorizontal(false); this.scrollbar.setHorizontal(false);
@@ -78,7 +80,7 @@ public class G2DTableControlImpl extends G2DBaseControlImpl implements TableCont
this.rebuildBufferedImage(); this.rebuildBufferedImage();
this.initControl(); this.initControl();
this.control.setDirty(false); this.control.setDirty(false);
this.updateAllComponentZOrder();
} }
this.control.paint(this.bufferedImageGraphics); this.control.paint(this.bufferedImageGraphics);

View File

@@ -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 @Override
@@ -98,6 +100,7 @@ public class G2DTextfieldControlImpl extends G2DBaseControlImpl implements Textf
else { else {
((CustomTextField) this.textComponent).setDirty(false); ((CustomTextField) this.textComponent).setDirty(false);
} }
this.updateAllComponentZOrder();
} }
super.render(); super.render();

View File

@@ -136,9 +136,11 @@ public class G2DTextfieldControlImpl2 extends G2DBaseControlImpl implements Text
this.textComponent.setName(this.id); this.textComponent.setName(this.id);
this.initControl(); this.initControl();
final var mainFrame = ((G2DRenderer) this.rendererData.getRenderer()).getMainFrame();
if (this.multiLine) { if (this.multiLine) {
// ((G2DRenderer) this.RendererData.getRenderer()).getMainFrame().add(this.scrollPane); // mainFrame.add(this.scrollPane);
((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().add(this.textComponent); mainFrame.add(this.textComponent);
this.updateAllComponentZOrder();
this.scrollbar.setColor(RgbaColor.LIGHT_GRAY); this.scrollbar.setColor(RgbaColor.LIGHT_GRAY);
this.scrollbar.setHorizontal(false); this.scrollbar.setHorizontal(false);
@@ -147,7 +149,8 @@ public class G2DTextfieldControlImpl2 extends G2DBaseControlImpl implements Text
this.scrollbar.build(); this.scrollbar.build();
} }
else { 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 { else {
((CustomJTextField) this.textComponent).setDirty(false); ((CustomJTextField) this.textComponent).setDirty(false);
} }
this.updateAllComponentZOrder();
} }
if (this.multiLine) { if (this.multiLine) {