Skip to content

Commit

Permalink
Add RenderGuiLayerEvent.Pre#isGoingToRender(), allowing to determine …
Browse files Browse the repository at this point in the history
…whenever layer is actually going to render on screen

Additionally, adds new RegisterGuiLayersEvent API methods, allowing to specify own "rendering condition" which is visible to other mods in isGoingToRender()
  • Loading branch information
DBotThePony committed Aug 31, 2024
1 parent 1cdd210 commit a192910
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.google.common.base.Preconditions;
import java.util.List;
import java.util.Objects;
import java.util.function.BooleanSupplier;
import java.util.stream.IntStream;
import net.minecraft.client.gui.LayeredDraw;
import net.minecraft.resources.ResourceLocation;
Expand All @@ -25,7 +26,7 @@
*
* <p>See also {@link RenderGuiLayerEvent} to intercept rendering of registered layers.
*
* <p>This event is not {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.
* <p>This event is not {@linkplain ICancellableEvent cancellable}.
*
* <p>This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
*/
Expand All @@ -40,49 +41,110 @@ public RegisterGuiLayersEvent(List<GuiLayerManager.NamedLayer> layers) {
/**
* Registers a layer that renders below all others.
*
* <p>If your layer has custom condition(s) when to draw, please consider using method with {@linkplain BooleanSupplier} argument.</p>
*
* @param id A unique resource id for this layer
* @param layer The layer
*/
public void registerBelowAll(ResourceLocation id, LayeredDraw.Layer layer) {
register(Ordering.BEFORE, null, id, layer);
register(Ordering.BEFORE, null, id, layer, () -> true);
}

/**
* Registers a layer that renders below all others.
*
* @param id A unique resource id for this layer
* @param layer The layer
* @param shouldBeDrawn Predicate, determining whenever layer should be drawn. Value returned by this function will be
* visible to other mods in {@linkplain RenderGuiLayerEvent.Pre#isGoingToRender()} and {@linkplain RenderGuiLayerEvent.Post#actuallyRendered()}}
*/
public void registerBelowAll(ResourceLocation id, LayeredDraw.Layer layer, BooleanSupplier shouldBeDrawn) {
register(Ordering.BEFORE, null, id, layer, shouldBeDrawn);
}

/**
* Registers a layer that renders below another.
*
* <p>If your layer has custom condition(s) when to draw, please consider using method with {@linkplain BooleanSupplier} argument.</p>
*
* @param other The id of the layer to render below. This must be a layer you have already registered or one of the
* {@link VanillaGuiLayers vanilla layers}. Do not use other mods' layers.
* @param id A unique resource id for this layer
* @param layer The layer
*/
public void registerBelow(ResourceLocation other, ResourceLocation id, LayeredDraw.Layer layer) {
register(Ordering.BEFORE, other, id, layer);
register(Ordering.BEFORE, other, id, layer, () -> true);
}

/**
* Registers an layer that renders above another.
* Registers a layer that renders below another.
*
* @param other The id of the layer to render below. This must be a layer you have already registered or one of the
* {@link VanillaGuiLayers vanilla layers}. Do not use other mods' layers.
* @param id A unique resource id for this layer
* @param layer The layer
* @param shouldBeDrawn Predicate, determining whenever layer should be drawn. Value returned by this function will be
* visible to other mods in {@linkplain RenderGuiLayerEvent.Pre#isGoingToRender()} and {@linkplain RenderGuiLayerEvent.Post#actuallyRendered()}}
*/
public void registerBelow(ResourceLocation other, ResourceLocation id, LayeredDraw.Layer layer, BooleanSupplier shouldBeDrawn) {
register(Ordering.BEFORE, other, id, layer, shouldBeDrawn);
}

/**
* Registers a layer that renders above another.
*
* <p>If your layer has custom condition(s) when to draw, please consider using method with {@linkplain BooleanSupplier} argument.</p>
*
* @param other The id of the layer to render above. This must be a layer you have already registered or one of the
* {@link VanillaGuiLayers vanilla layers}. Do not use other mods' layers.
* @param id A unique resource id for this layer
* @param layer The layer
*/
public void registerAbove(ResourceLocation other, ResourceLocation id, LayeredDraw.Layer layer) {
register(Ordering.AFTER, other, id, layer);
register(Ordering.AFTER, other, id, layer, () -> true);
}

/**
* Registers a layer that renders above another.
*
* @param other The id of the layer to render above. This must be a layer you have already registered or one of the
* {@link VanillaGuiLayers vanilla layers}. Do not use other mods' layers.
* @param id A unique resource id for this layer
* @param layer The layer
* @param shouldBeDrawn Predicate, determining whenever layer should be drawn. Value returned by this function will be
* visible to other mods in {@linkplain RenderGuiLayerEvent.Pre#isGoingToRender()} and {@linkplain RenderGuiLayerEvent.Post#actuallyRendered()}}
*/
public void registerAbove(ResourceLocation other, ResourceLocation id, LayeredDraw.Layer layer, BooleanSupplier shouldBeDrawn) {
register(Ordering.AFTER, other, id, layer, shouldBeDrawn);
}

/**
* Registers a layer that renders above all others.
*
* <p>If your layer has custom condition(s) when to draw, please consider using method with {@linkplain BooleanSupplier} argument.</p>
*
* @param id A unique resource id for this layer
* @param layer The layer
*/
public void registerAboveAll(ResourceLocation id, LayeredDraw.Layer layer) {
register(Ordering.AFTER, null, id, layer);
register(Ordering.AFTER, null, id, layer, () -> true);
}

private void register(Ordering ordering, @Nullable ResourceLocation other, ResourceLocation key, LayeredDraw.Layer layer) {
/**
* Registers a layer that renders above all others.
*
* @param id A unique resource id for this layer
* @param layer The layer
* @param shouldBeDrawn Predicate, determining whenever layer should be drawn. Value returned by this function will be
* visible to other mods in {@linkplain RenderGuiLayerEvent.Pre#isGoingToRender()} and {@linkplain RenderGuiLayerEvent.Post#actuallyRendered()}}
*/
public void registerAboveAll(ResourceLocation id, LayeredDraw.Layer layer, BooleanSupplier shouldBeDrawn) {
register(Ordering.AFTER, null, id, layer, shouldBeDrawn);
}

private void register(Ordering ordering, @Nullable ResourceLocation other, ResourceLocation key, LayeredDraw.Layer layer, BooleanSupplier shouldBeDrawn) {
Objects.requireNonNull(key);

for (var namedLayer : layers) {
Preconditions.checkArgument(!namedLayer.name().equals(key), "Layer already registered: " + key);
}
Expand All @@ -101,7 +163,7 @@ private void register(Ordering ordering, @Nullable ResourceLocation other, Resou
insertPosition = otherIndex.getAsInt() + (ordering == Ordering.BEFORE ? 0 : 1);
}

layers.add(insertPosition, new GuiLayerManager.NamedLayer(key, layer));
layers.add(insertPosition, new GuiLayerManager.NamedLayer(key, layer, shouldBeDrawn));
}

private enum Ordering {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ public LayeredDraw.Layer getLayer() {

/**
* Fired <b>before</b> a GUI layer is rendered to the screen.
* <br>The event is fired <b>regardless</b> if it being actually drawn on screen
* (use {@linkplain Pre#isGoingToRender()} to determine this)
*
* <p>This event is {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.
* <p>This event is {@linkplain ICancellableEvent cancellable}.
* If this event is cancelled, then the layer will not be rendered, and the corresponding {@link Post} event will
* not be fired.</p>
*
Expand All @@ -68,24 +70,46 @@ public LayeredDraw.Layer getLayer() {
* @see Post
*/
public static class Pre extends RenderGuiLayerEvent implements ICancellableEvent {
private final boolean isGoingToRender;

@ApiStatus.Internal
public Pre(GuiGraphics guiGraphics, DeltaTracker partialTick, ResourceLocation name, LayeredDraw.Layer layer) {
public Pre(GuiGraphics guiGraphics, DeltaTracker partialTick, ResourceLocation name, LayeredDraw.Layer layer, boolean isGoingToRender) {
super(guiGraphics, partialTick, name, layer);
this.isGoingToRender = isGoingToRender;
}

/**
* Whenever element is actually going to render on screen if event is not canceled.
* <p>This is going to return <i>false</i> if element is currently not applicable for rendering, such
* as trying to render player health while player is in creative mode, or if Minecraft GUI is hidden by pressing F1.</p>
*/
public boolean isGoingToRender() {
return isGoingToRender;
}
}

/**
* Fired <b>after</b> a GUI layer is rendered to the screen, if the corresponding {@link Pre} is not cancelled.
*
* <p>This event is not {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.</p>
* <p>This event is not {@linkplain ICancellableEvent cancellable}.</p>
*
* <p>This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus},
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
*/
public static class Post extends RenderGuiLayerEvent {
private final boolean actuallyRendered;

@ApiStatus.Internal
public Post(GuiGraphics guiGraphics, DeltaTracker partialTick, ResourceLocation name, LayeredDraw.Layer layer) {
public Post(GuiGraphics guiGraphics, DeltaTracker partialTick, ResourceLocation name, LayeredDraw.Layer layer, boolean actuallyRendered) {
super(guiGraphics, partialTick, name, layer);
this.actuallyRendered = actuallyRendered;
}

/**
* @see Pre#isGoingToRender()
*/
public boolean actuallyRendered() {
return actuallyRendered;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,54 +32,54 @@ public class GuiLayerManager {
private final List<NamedLayer> layers = new ArrayList<>();
private boolean initialized = false;

public record NamedLayer(ResourceLocation name, LayeredDraw.Layer layer) {}
public record NamedLayer(ResourceLocation name, LayeredDraw.Layer layer, BooleanSupplier shouldRender) {}

public GuiLayerManager add(ResourceLocation name, LayeredDraw.Layer layer) {
this.layers.add(new NamedLayer(name, layer));
return this.add(name, layer, () -> true);
}

public GuiLayerManager add(ResourceLocation name, LayeredDraw.Layer layer, BooleanSupplier shouldRender) {
this.layers.add(new NamedLayer(name, layer, shouldRender));
return this;
}

public GuiLayerManager add(GuiLayerManager child, BooleanSupplier shouldRender) {
// Flatten the layers to allow mods to insert layers between vanilla layers.
for (var entry : child.layers) {
add(entry.name(), (guiGraphics, partialTick) -> {
if (shouldRender.getAsBoolean()) {
entry.layer().render(guiGraphics, partialTick);
}
});
var shouldRenderLayer = entry.shouldRender();
add(entry.name(), entry.layer(), () -> shouldRender.getAsBoolean() && shouldRenderLayer.getAsBoolean());
}

return this;
}

public void render(GuiGraphics guiGraphics, DeltaTracker partialTick) {
if (NeoForge.EVENT_BUS.post(new RenderGuiEvent.Pre(guiGraphics, partialTick)).isCanceled()) {
return;
}
if (!NeoForge.EVENT_BUS.post(new RenderGuiEvent.Pre(guiGraphics, partialTick)).isCanceled()) {
guiGraphics.pose().pushPose();

renderInner(guiGraphics, partialTick);
for (var layer : this.layers) {
var shouldRender = layer.shouldRender().getAsBoolean();

NeoForge.EVENT_BUS.post(new RenderGuiEvent.Post(guiGraphics, partialTick));
}

private void renderInner(GuiGraphics guiGraphics, DeltaTracker partialTick) {
guiGraphics.pose().pushPose();
// TODO: Reconsider this behavior in future version during breaking change window
if (!NeoForge.EVENT_BUS.post(new RenderGuiLayerEvent.Pre(guiGraphics, partialTick, layer.name(), layer.layer(), shouldRender)).isCanceled()) {
if (shouldRender) layer.layer().render(guiGraphics, partialTick);
NeoForge.EVENT_BUS.post(new RenderGuiLayerEvent.Post(guiGraphics, partialTick, layer.name(), layer.layer(), shouldRender));
}

for (var layer : this.layers) {
if (!NeoForge.EVENT_BUS.post(new RenderGuiLayerEvent.Pre(guiGraphics, partialTick, layer.name(), layer.layer())).isCanceled()) {
layer.layer().render(guiGraphics, partialTick);
NeoForge.EVENT_BUS.post(new RenderGuiLayerEvent.Post(guiGraphics, partialTick, layer.name(), layer.layer()));
guiGraphics.pose().translate(0.0F, 0.0F, Z_SEPARATION);
}

guiGraphics.pose().translate(0.0F, 0.0F, Z_SEPARATION);
}
guiGraphics.pose().popPose();

guiGraphics.pose().popPose();
NeoForge.EVENT_BUS.post(new RenderGuiEvent.Post(guiGraphics, partialTick));
}
}

public void initModdedLayers() {
if (initialized) {
throw new IllegalStateException("Duplicate initialization of NamedLayeredDraw");
}

initialized = true;
ModLoader.postEvent(new RegisterGuiLayersEvent(this.layers));
}
Expand Down

0 comments on commit a192910

Please sign in to comment.