Skip to content

ComposeContainerScreen

abstract class ComposeContainerScreen<T : ComposeContainerMenuBase<T>>(
    menu: T, 
    playerInventory: Inventory, 
    title: Component, 
    val asynchronous: Boolean = true
) : AbstractContainerScreen<T> , CoroutineScope, SlotLayerDepthProvider, SlotHighlightClipProvider, ScreenOverlayDeferral, ComposeIdleAware, LayerManagerProvider

A Compose-driven AbstractContainerScreen with layer support, async recomposition, and vanilla Slot rendering kept in sync with the Compose-reported SlotGroup layout.

Behaves like ComposeScreen but additionally bridges vanilla's container/slot machinery: the base layer (layer 0) is rendered from renderBg so it draws under vanilla's slots, and any additional layers (modals) render on top from render via renderSlot/slotClipRect clipping so scrolled-out-of-view slots don't paint over unrelated content.

Extend this class and call start inside your init() override, the same way as ComposeScreen. Works uniformly for both ComposeBlockContainerMenu (BlockEntity-backed) and ComposeItemContainerMenu (ItemStack-backed) subclasses - nothing here is holder-specific.

Parameters

  • T: The concrete ComposeContainerMenuBase subclass driving this screen.

  • menu: The container menu instance for this screen.

  • playerInventory: The opening player's inventory.

  • title: The screen title passed to the vanilla AbstractContainerScreen constructor.

  • asynchronous: When true (default), recomposition runs off the main thread and the result is joined at the start of the next frame for smooth, non-blocking updates. Set to false to force synchronous recomposition (simpler but may stutter).

Constructors

ComposeContainerScreen

constructor(
    menu: T, 
    playerInventory: Inventory, 
    title: Component, 
    asynchronous: Boolean = true
)

Parameters

  • T: The concrete ComposeContainerMenuBase subclass driving this screen.

  • menu: The container menu instance for this screen.

  • playerInventory: The opening player's inventory.

  • title: The screen title passed to the vanilla AbstractContainerScreen constructor.

  • asynchronous: When true (default), recomposition runs off the main thread and the result is joined at the start of the next frame for smooth, non-blocking updates. Set to false to force synchronous recomposition (simpler but may stutter).

Types

Companion

object Companion

Properties

asynchronous

Parameters

  • asynchronous: When true (default), recomposition runs off the main thread and the result is joined at the start of the next frame for smooth, non-blocking updates. Set to false to force synchronous recomposition (simpler but may stutter).

backgroundMusic

@get:



Nullable



open val backgroundMusic: Music?

coroutineContext

currentFocusPath

@get:



Nullable



open val currentFocusPath: ComponentPath?

focused

@get:



Nullable



open var focused: GuiEventListener?

guiLeft

open override val guiLeft: Int

guiTop

open override val guiTop: Int

height

var height: Int

inventoryLabelPos

inventoryLabelX/inventoryLabelY expressed as an absolute-screen IntCoordinates pair.

isDragging

isFocused

open var isFocused: Boolean

isPauseScreen

open override val isPauseScreen: Boolean

layerManager

lateinit override var layerManager: LayerStackManager

The layer stack owned by this host.

open override val menu: T?

minecraft

open val minecraft: Minecraft?

narrationMessage

open val narrationMessage: Component?

rectangle

open val rectangle: ScreenRectangle?

renderables

val renderables: List<Renderable?>?

slotUnderMouse

@get:



Nullable



open override val slotUnderMouse: @Nullable



 Slot?

tabOrderGroup

open val tabOrderGroup: Int

title

open val title: Component?

titleLabelPos

titleLabelX/titleLabelY expressed as an absolute-screen IntCoordinates pair.

width

var width: Int

xSize

open override val xSize: Int

ySize

open override val ySize: Int

Functions

added

open override fun added()

Restarts the Compose runtime with contentFn if a prior removed disposed it - vanilla's Minecraft.setScreen() calls removed on this screen for any swap-away, including one a caller intends to reverse later by handing this exact instance back to setScreen() again (a recipe viewer's "view recipe" navigation, say), not just a genuine close. Without this, such a screen never renders its Compose content again once reactivated - only vanilla's own slot/title rendering (untouched by any of this) keeps working, which is what actually surfaces the bug: the screen looks blank apart from its slots after returning from a recipe lookup.

added rather than the no-arg init() hook - Screen.init(Minecraft, int, int) only calls that hook down a much more conditional path (gated by vanilla's own initialized flag and, on NeoForge, a cancellable ScreenEvent.Init.Pre), so it doesn't reliably fire on every reactivation. Minecraft.setScreen() calls added unconditionally on every activation, fresh construction and reactivation alike - confirmed against the decompiled source, not assumed. A no-op on the very first added (right after construction), since composeDisposed starts false until a real removed call has actually happened.

afterKeyboardAction

open fun afterKeyboardAction()

afterMouseAction

open fun afterMouseAction()

afterMouseMove

open fun afterMouseMove()

charTyped

open override fun charTyped(codePoint: Char, modifiers: Int): Boolean

children

open override fun children(): List<GuiEventListener>

clearDraggingState

open fun clearDraggingState()

clearFocus

open fun clearFocus()

defersScreenOverlays

open override fun defersScreenOverlays(): Boolean

Only once something is actually drawn over the slot loop. With no layer open there is nothing for the highlight or the carried stack to end up behind, so vanilla's own inline draw is left exactly as it is - this costs a plain screen nothing and can regress nothing.

getChildAt

open fun getChildAt(mouseX: Double, mouseY: Double): Optional<GuiEventListener?>?

getMenu

open fun getMenu(): T?

getSlotColor

open fun getSlotColor(index: Int): Int

handleComponentClicked

open fun handleComponentClicked(@Nullable



 style: Style?): Boolean

handleDelayedNarration

init

fun init(
    minecraft: Minecraft?, 
    width: Int, 
    height: Int
)

isComposeIdle

open override fun isComposeIdle(): Boolean

true when there is no snapshot-write notification, frame request, or recompose job pending.

isMouseOver

open fun isMouseOver(mouseX: Double, mouseY: Double): Boolean

keyPressed

open override fun keyPressed(
    keyCode: Int, 
    scanCode: Int, 
    modifiers: Int
): Boolean

keyReleased

open override fun keyReleased(
    keyCode: Int, 
    scanCode: Int, 
    modifiers: Int
): Boolean

mouseClicked

open override fun mouseClicked(
    mouseX: Double, 
    mouseY: Double, 
    button: Int
): Boolean

mouseDragged

open override fun mouseDragged(
    mouseX: Double, 
    mouseY: Double, 
    button: Int, 
    dragX: Double, 
    dragY: Double
): Boolean

mouseMoved

open override fun mouseMoved(mouseX: Double, mouseY: Double)

mouseReleased

open override fun mouseReleased(
    mouseX: Double, 
    mouseY: Double, 
    button: Int
): Boolean

mouseScrolled

open override fun mouseScrolled(
    mouseX: Double, 
    mouseY: Double, 
    scrollX: Double, 
    scrollY: Double
): Boolean

nextFocusPath

@Nullable



open fun nextFocusPath(event: FocusNavigationEvent?): ComponentPath?

onClose

open override fun onClose()

onFilesDrop

open fun onFilesDrop(packs: List<Path?>?)

removed

open override fun removed()

removeWidget

open fun removeWidget(listener: GuiEventListener?)

render

open override fun render(
    guiGraphics: GuiGraphics, 
    mouseX: Int, 
    mouseY: Int, 
    partialTick: Float
)

renderBackground

open fun renderBackground(
    guiGraphics: GuiGraphics?, 
    mouseX: Int, 
    mouseY: Int, 
    partialTick: Float
)

renderNodes

open fun renderNodes(
    baseLayer: Boolean, 
    guiGraphics: GuiGraphics, 
    mouseX: Int, 
    mouseY: Int, 
    partialTick: Float
)

Measures and renders all active layers.

In async mode the previous recompose job is joined before rendering, then a new job is launched if there are pending frame waiters.

renderTransparentBackground

open fun renderTransparentBackground(guiGraphics: GuiGraphics?)

renderWithTooltip

fun renderWithTooltip(
    guiGraphics: GuiGraphics?, 
    mouseX: Int, 
    mouseY: Int, 
    partialTick: Float
)

resize

open override fun resize(
    minecraft: Minecraft, 
    width: Int, 
    height: Int
)

setTooltipForNextRenderPass

open fun setTooltipForNextRenderPass(tooltip: List<FormattedCharSequence?>?)
open fun setTooltipForNextRenderPass(tooltip: Component?)
open fun setTooltipForNextRenderPass(
    tooltip: List<FormattedCharSequence?>?, 
    positioner: ClientTooltipPositioner?, 
    override: Boolean
)
open fun setTooltipForNextRenderPass(
    tooltip: Tooltip?, 
    positioner: ClientTooltipPositioner?, 
    override: Boolean
)

shouldCloseOnEsc

slotHighlightClipRect

open override fun slotHighlightClipRect(x: Int, y: Int): IntRect?

Called by net.kernelpanicsoft.archie.mixin.client.gui.AbstractContainerScreenMixin (via net.kernelpanicsoft.archie.gui.access.SlotHighlightClipProvider) to clip the hover-highlight overlay the same way renderSlot clips the item icon - vanilla only exposes a static renderSlotHighlight(GuiGraphics, x, y, blitOffset) with no per-slot override point, so this has to be reached from a mixin redirect instead of override.

slotRenderLayerOffset

open override fun slotRenderLayerOffset(slot: Slot): Float?

tick

fun tick()

triggerImmediateNarration

open fun triggerImmediateNarration(onlyNarrateNew: Boolean)

updateNarratorStatus

open fun updateNarratorStatus(narratorEnabled: Boolean)