Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RenderGuiLayerEvent.Pre#isGoingToRender(), allowing to determine whenever layer is actually going to render on screen #1505

Open
wants to merge 3 commits into
base: 1.21.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 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 an layer that renders above another.
* 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