feat(container): Dragging and adjustIfOutsideViewport
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
package de.extio.game_engine.renderer.container;
|
package de.extio.game_engine.renderer.container;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -11,17 +15,23 @@ import de.extio.game_engine.renderer.RendererControl;
|
|||||||
import de.extio.game_engine.renderer.model.RenderingBo;
|
import de.extio.game_engine.renderer.model.RenderingBo;
|
||||||
import de.extio.game_engine.renderer.model.bo.HorizontalAlignment;
|
import de.extio.game_engine.renderer.model.bo.HorizontalAlignment;
|
||||||
import de.extio.game_engine.renderer.model.bo.VerticalAlignment;
|
import de.extio.game_engine.renderer.model.bo.VerticalAlignment;
|
||||||
|
import de.extio.game_engine.renderer.model.event.MouseClickEvent;
|
||||||
|
import de.extio.game_engine.renderer.model.event.MouseMoveEvent;
|
||||||
import de.extio.game_engine.renderer.model.event.ViewportResizeEvent;
|
import de.extio.game_engine.renderer.model.event.ViewportResizeEvent;
|
||||||
import de.extio.game_engine.renderer.work.RendererWorkingSet;
|
import de.extio.game_engine.renderer.work.RendererWorkingSet;
|
||||||
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||||
|
import de.extio.game_engine.spatial2.SpatialUtils2;
|
||||||
import de.extio.game_engine.spatial2.model.Area2;
|
import de.extio.game_engine.spatial2.model.Area2;
|
||||||
import de.extio.game_engine.spatial2.model.CoordI2;
|
import de.extio.game_engine.spatial2.model.CoordI2;
|
||||||
|
import de.extio.game_engine.spatial2.model.ImmutableCoordI2;
|
||||||
import de.extio.game_engine.spatial2.model.MutableCoordI2;
|
import de.extio.game_engine.spatial2.model.MutableCoordI2;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
public class Container extends AbstractClientModule implements InitializingBean {
|
public class Container extends AbstractClientModule implements InitializingBean {
|
||||||
|
|
||||||
|
protected final static List<Container> DISPLAYED_CONTAINERS = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
protected final ModuleService moduleService;
|
protected final ModuleService moduleService;
|
||||||
|
|
||||||
protected final RenderingBoPool renderingBoPool;
|
protected final RenderingBoPool renderingBoPool;
|
||||||
@@ -39,6 +49,8 @@ public class Container extends AbstractClientModule implements InitializingBean
|
|||||||
protected boolean draggable;
|
protected boolean draggable;
|
||||||
|
|
||||||
protected boolean dragging;
|
protected boolean dragging;
|
||||||
|
|
||||||
|
protected CoordI2 dragPrevPosition;
|
||||||
|
|
||||||
protected HorizontalAlignment horizontalAlignment = HorizontalAlignment.CENTER;
|
protected HorizontalAlignment horizontalAlignment = HorizontalAlignment.CENTER;
|
||||||
|
|
||||||
@@ -60,24 +72,144 @@ public class Container extends AbstractClientModule implements InitializingBean
|
|||||||
@Override
|
@Override
|
||||||
public void onActivate() {
|
public void onActivate() {
|
||||||
this.eventService.register(ViewportResizeEvent.class, this.id, this::onViewportResize);
|
this.eventService.register(ViewportResizeEvent.class, this.id, this::onViewportResize);
|
||||||
|
this.eventService.register(MouseMoveEvent.class, this.id, this::onMouseMoveEvent);
|
||||||
|
this.eventService.register(MouseClickEvent.class, this.id, this::onMouseClickEvent);
|
||||||
this.onViewportResize(null);
|
this.onViewportResize(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onShow() {
|
public void onShow() {
|
||||||
|
DISPLAYED_CONTAINERS.add(this);
|
||||||
this.draw();
|
this.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeactivate() {
|
public void onHide() {
|
||||||
|
DISPLAYED_CONTAINERS.remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onViewportResize(final ViewportResizeEvent event) {
|
protected void onViewportResize(final ViewportResizeEvent event) {
|
||||||
this.setNormalizedPosition(this.normalizedArea.getPosition());
|
this.setNormalizedPosition(this.normalizedArea.getPosition());
|
||||||
this.setNormalizedDimension(this.normalizedArea.getDimension());
|
this.setNormalizedDimension(this.normalizedArea.getDimension());
|
||||||
|
this.adjustIfOutsideViewport();
|
||||||
this.draw();
|
this.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean adjustIfOutsideViewport() {
|
||||||
|
final var viewportDimension = this.rendererControl.getEffectiveViewportDimension();
|
||||||
|
final var position = this.area.getPosition();
|
||||||
|
final var dimension = this.area.getDimension();
|
||||||
|
|
||||||
|
final var containerArea = this.area.getDimension().getX() * this.area.getDimension().getY();
|
||||||
|
final var intersectionArea = this.calculateIntersectionArea(position, dimension, viewportDimension);
|
||||||
|
final var intersectionPercentage = containerArea > 0 ? (double) intersectionArea / containerArea : 0.0;
|
||||||
|
|
||||||
|
if (intersectionPercentage >= 0.1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int adjustedX = position.getX();
|
||||||
|
int adjustedY = position.getY();
|
||||||
|
|
||||||
|
if (position.getX() >= viewportDimension.getX()) {
|
||||||
|
adjustedX = viewportDimension.getX() - dimension.getX();
|
||||||
|
} else if (position.getX() + dimension.getX() <= 0) {
|
||||||
|
adjustedX = 0;
|
||||||
|
} else if (position.getX() < 0) {
|
||||||
|
adjustedX = 0;
|
||||||
|
} else if (position.getX() + dimension.getX() > viewportDimension.getX()) {
|
||||||
|
adjustedX = viewportDimension.getX() - dimension.getX();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.getY() >= viewportDimension.getY()) {
|
||||||
|
adjustedY = viewportDimension.getY() - dimension.getY();
|
||||||
|
} else if (position.getY() + dimension.getY() <= 0) {
|
||||||
|
adjustedY = 0;
|
||||||
|
} else if (position.getY() < 0) {
|
||||||
|
adjustedY = 0;
|
||||||
|
} else if (position.getY() + dimension.getY() > viewportDimension.getY()) {
|
||||||
|
adjustedY = viewportDimension.getY() - dimension.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
final var normalizedX = this.calculateNormalizedX(adjustedX);
|
||||||
|
final var normalizedY = this.calculateNormalizedY(adjustedY);
|
||||||
|
this.normalizedArea.getPosition().setXY(normalizedX, normalizedY);
|
||||||
|
this.area.getPosition().setXY(adjustedX, adjustedY);
|
||||||
|
|
||||||
|
this.LOGGER.debug("Adjusted container {} position to {},{} to remain visible in viewport", this.id, adjustedX, adjustedY);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateIntersectionArea(final CoordI2 position, final CoordI2 dimension, final CoordI2 viewportDimension) {
|
||||||
|
final int x1 = Math.max(0, position.getX());
|
||||||
|
final int y1 = Math.max(0, position.getY());
|
||||||
|
final int x2 = Math.min(viewportDimension.getX(), position.getX() + dimension.getX());
|
||||||
|
final int y2 = Math.min(viewportDimension.getY(), position.getY() + dimension.getY());
|
||||||
|
|
||||||
|
final int width = Math.max(0, x2 - x1);
|
||||||
|
final int height = Math.max(0, y2 - y1);
|
||||||
|
|
||||||
|
return width * height;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int calculateNormalizedX(final int actualX) {
|
||||||
|
return switch (this.horizontalAlignment) {
|
||||||
|
case LEFT -> actualX;
|
||||||
|
case CENTER -> actualX - (this.rendererControl.getEffectiveViewportDimension().getX() - RendererControl.REFERENCE_RESOLUTION.getX()) / 2;
|
||||||
|
case RIGHT -> actualX - (this.rendererControl.getEffectiveViewportDimension().getX() - RendererControl.REFERENCE_RESOLUTION.getX());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int calculateNormalizedY(final int actualY) {
|
||||||
|
return switch (this.verticalAlignment) {
|
||||||
|
case TOP -> actualY;
|
||||||
|
case CENTER -> actualY - (this.rendererControl.getEffectiveViewportDimension().getY() - RendererControl.REFERENCE_RESOLUTION.getY()) / 2;
|
||||||
|
case BOTTOM -> actualY - (this.rendererControl.getEffectiveViewportDimension().getY() - RendererControl.REFERENCE_RESOLUTION.getY());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onMouseMoveEvent(final MouseMoveEvent event) {
|
||||||
|
if (this.draggable && event.isDrag()) {
|
||||||
|
Container foregroundContainer = null;
|
||||||
|
synchronized (DISPLAYED_CONTAINERS) {
|
||||||
|
for (final var container : DISPLAYED_CONTAINERS) {
|
||||||
|
if (container.dragging) {
|
||||||
|
foregroundContainer = container;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (SpatialUtils2.intersects(container.area.getPosition(), container.area.getDimension(), event.getCoord(), ImmutableCoordI2.one())) {
|
||||||
|
// TODO z-index handling
|
||||||
|
foregroundContainer = container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (foregroundContainer == this) {
|
||||||
|
if (!this.dragging) {
|
||||||
|
this.dragging = true;
|
||||||
|
this.dragPrevPosition = null;
|
||||||
|
this.LOGGER.debug("Started dragging container {}", this.id);
|
||||||
|
}
|
||||||
|
if (this.dragPrevPosition != null) {
|
||||||
|
final var delta = event.getCoord().substract(this.dragPrevPosition);
|
||||||
|
if (! ImmutableCoordI2.zero().equals(delta)) {
|
||||||
|
this.setNormalizedPosition(this.getNormalizedPosition().add(delta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.dragPrevPosition = event.getCoord();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onMouseClickEvent(final MouseClickEvent event) {
|
||||||
|
if (! event.isPressed() && this.dragging) {
|
||||||
|
this.dragging = false;
|
||||||
|
this.dragPrevPosition = null;
|
||||||
|
if (this.adjustIfOutsideViewport()) {
|
||||||
|
this.draw();
|
||||||
|
}
|
||||||
|
this.LOGGER.debug("Stopped dragging container {}", this.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override in subclasses, but make sure to call super.draw() at the end if overridden
|
* Override in subclasses, but make sure to call super.draw() at the end if overridden
|
||||||
@@ -120,7 +252,6 @@ public class Container extends AbstractClientModule implements InitializingBean
|
|||||||
case BOTTOM -> this.area.getPosition().setY(normalizedPosition.getY() + this.rendererControl.getEffectiveViewportDimension().getY() - RendererControl.REFERENCE_RESOLUTION.getY());
|
case BOTTOM -> this.area.getPosition().setY(normalizedPosition.getY() + this.rendererControl.getEffectiveViewportDimension().getY() - RendererControl.REFERENCE_RESOLUTION.getY());
|
||||||
}
|
}
|
||||||
this.draw();
|
this.draw();
|
||||||
this.LOGGER.debug("Effective viewport dimension: {}", this.rendererControl.getEffectiveViewportDimension());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CoordI2 getNormalizedPosition() {
|
public CoordI2 getNormalizedPosition() {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.WindowCloseButt
|
|||||||
import de.extio.game_engine.renderer.model.color.RgbaColor;
|
import de.extio.game_engine.renderer.model.color.RgbaColor;
|
||||||
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||||
import de.extio.game_engine.renderer.model.bo.DrawWindowRenderingBo;
|
import de.extio.game_engine.renderer.model.bo.DrawWindowRenderingBo;
|
||||||
import de.extio.game_engine.spatial2.WorldUtils2;
|
import de.extio.game_engine.spatial2.SpatialUtils2;
|
||||||
import de.extio.game_engine.spatial2.model.Area2;
|
import de.extio.game_engine.spatial2.model.Area2;
|
||||||
import de.extio.game_engine.spatial2.model.CoordD2;
|
import de.extio.game_engine.spatial2.model.CoordD2;
|
||||||
import de.extio.game_engine.spatial2.model.CoordI2;
|
import de.extio.game_engine.spatial2.model.CoordI2;
|
||||||
@@ -82,7 +82,7 @@ public class Window implements HasPositionAndDimension2, AutoCloseable {
|
|||||||
// return false;
|
// return false;
|
||||||
synchronized (WINDOWS) {
|
synchronized (WINDOWS) {
|
||||||
for (final Window window : WINDOWS) {
|
for (final Window window : WINDOWS) {
|
||||||
if (WorldUtils2.intersects(window, area)) {
|
if (SpatialUtils2.intersects(window, area)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ public class Window implements HasPositionAndDimension2, AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean intersects(final CoordI2 coord) {
|
public boolean intersects(final CoordI2 coord) {
|
||||||
return WorldUtils2.intersects(this.area.getPosition(), this.area.getDimension(), coord, ImmutableCoordI2.one());
|
return SpatialUtils2.intersects(this.area.getPosition(), this.area.getDimension(), coord, ImmutableCoordI2.one());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(final List<RenderingBo> renderingBo, final BiConsumer<List<RenderingBo>, RenderingBo> consumer) {
|
public void render(final List<RenderingBo> renderingBo, final BiConsumer<List<RenderingBo>, RenderingBo> consumer) {
|
||||||
|
|||||||
Reference in New Issue
Block a user