Class GuiRenderer
It is the declarative framework's core engine, with the main responsibilities of:
- managing the Element tree's lifecycle
- scheduling rebuilds (with frame coalescing)
- running the diff algorithm
- applying changes to the actual Inventory
Workflow:
1. Initialize: createRootElement → build the RenderNode tree 2. Render: diff → apply to Inventory 3. Update: setState → scheduleBuild → rebuild → diff → apply
- Since:
- 6.2.0
- Version:
- 1.0.0
- Author:
- UltiTools Team
-
Field Summary
FieldsModifier and TypeFieldDescriptionprivate final RenderNodeDifferprivate final mc.obliviate.inventory.Guiprivate @Nullable List<RenderNode> private final org.bukkit.entity.Playerprivate @Nullable BuildContextprivate @Nullable Elementprivate final GuiScheduler -
Constructor Summary
ConstructorsConstructorDescriptionGuiRenderer(@NotNull mc.obliviate.inventory.Gui gui, @NotNull org.bukkit.entity.Player player) Creates a GuiRenderer.GuiRenderer(@NotNull mc.obliviate.inventory.Gui gui, @NotNull org.bukkit.entity.Player player, @NotNull GuiScheduler scheduler) Creates a GuiRenderer with the given scheduler. -
Method Summary
Modifier and TypeMethodDescriptionprivate voidapplyDiff(@NotNull DiffResult diffResult) Applies the diff result to the Inventory.private voidclearSlot(int slot) Clears a slot.private @NotNull List<RenderNode> collectRenderNodes(@NotNull Element element) Collects every RenderNode (post-order traversal).private voidcollectRenderNodesRecursive(@NotNull Element element, @NotNull List<RenderNode> nodes) voiddispose()Destroys the renderer and releases its resources.@NotNull mc.obliviate.inventory.GuigetGui()Gets the associated Gui instance.@NotNull GuiSchedulerGets the scheduler.voidhandleClick(@NotNull org.bukkit.event.inventory.InventoryClickEvent event) Handles a click event.voidinitialize(@NotNull Supplier<Widget> widgetSupplier, @NotNull BuildContext context) Initializes the renderer.private voidCreates and mounts the root Element.private voidRuns a rebuild immediately (must be called on the main thread).private voidrebuildElement(@NotNull Element element) Recursively rebuilds the Element tree.voidSchedules a rebuild.private voidsetSlot(int slot, @Nullable mc.obliviate.inventory.Icon icon) Sets a slot's content.private voidupdateClickHandlers(@NotNull List<RenderNode> renderNodes) Updates the click-handler mapping.
-
Field Details
-
gui
private final mc.obliviate.inventory.Gui gui -
player
private final org.bukkit.entity.Player player -
scheduler
-
differ
-
rootElement
-
lastRenderNodes
-
widgetSupplier
-
rootContext
-
clickHandlers
-
-
Constructor Details
-
GuiRenderer
public GuiRenderer(@NotNull @NotNull mc.obliviate.inventory.Gui gui, @NotNull @NotNull org.bukkit.entity.Player player) Creates a GuiRenderer.- Parameters:
gui- the associated Gui instanceplayer- the player
-
GuiRenderer
public GuiRenderer(@NotNull @NotNull mc.obliviate.inventory.Gui gui, @NotNull @NotNull org.bukkit.entity.Player player, @NotNull @NotNull GuiScheduler scheduler) Creates a GuiRenderer with the given scheduler.- Parameters:
gui- the associated Gui instanceplayer- the playerscheduler- the scheduler
-
-
Method Details
-
initialize
public void initialize(@NotNull @NotNull Supplier<Widget> widgetSupplier, @NotNull @NotNull BuildContext context) Initializes the renderer.D-09 item 1: no longer accepts a Widget built once up front; instead it accepts a
Supplierthat is called again at the start of every frame, re-deriving the Widget tree from the current state -- this is where the framework'sUI = f(state)tagline first actually holds. Creating the root Element is deferred until inside the firstperformBuild()call, so the supplier is called exactly once on the first frame too, rather than once here and again inside performBuild().- Parameters:
widgetSupplier- the source re-evaluated every frame to produce the Widget treecontext- the build context (used for the root Element; later frames' rebuilds and remounts reuse the same one)
-
scheduleBuild
public void scheduleBuild()Schedules a rebuild.Uses frame coalescing, so multiple calls within a short window trigger only one rebuild.
-
performBuild
private void performBuild()Runs a rebuild immediately (must be called on the main thread).D-09 item 1:
widgetSupplieris called again at the start of every frame, and its result is fed to the already-mounted root Element (Element.update(com.ultikits.ultitools.abstracts.gui.declarative.core.Widget), whose base-class implementation now marks it dirty -- see D-09 item 2), rather than being built once atinitialize(java.util.function.Supplier<com.ultikits.ultitools.abstracts.gui.declarative.core.Widget>, com.ultikits.ultitools.abstracts.gui.declarative.core.BuildContext)time and never re-derived again. -
mountRoot
Creates and mounts the root Element.Shared by both the first build and the "supplier returned an incompatible type" case.
- Parameters:
widget- the root Widget
-
rebuildElement
Recursively rebuilds the Element tree.Guarded by
RenderDepthGuard(T-05-62 / #371): a module-authored tree deeper thanRenderDepthGuard.MAX_DEPTHraisesRenderDepthExceededExceptionhere rather than overflowing the JVM stack mid-frame.- Parameters:
element- the Element to rebuild
-
collectRenderNodes
Collects every RenderNode (post-order traversal).- Parameters:
element- the root Element- Returns:
- the list of RenderNodes
-
collectRenderNodesRecursive
private void collectRenderNodesRecursive(@NotNull @NotNull Element element, @NotNull @NotNull List<RenderNode> nodes) -
applyDiff
Applies the diff result to the Inventory.- Parameters:
diffResult- the diff result
-
updateClickHandlers
Updates the click-handler mapping.- Parameters:
renderNodes- the current list of RenderNodes
-
handleClick
public void handleClick(@NotNull @NotNull org.bukkit.event.inventory.InventoryClickEvent event) Handles a click event.D-09 item 4: obliviate-invs'
InvListener.onClickunconditionally callsGui.onClick(event)(i.e.DeclarativeGui.onClick(org.bukkit.event.inventory.InventoryClickEvent), which forwards here) before applying its owngetRawSlot()check -- this was confirmed from the jar's bytecode, not assumed. That means this method sees every click, including the player clicking their own inventory; it cannot assume the library has already filtered anything out.This uses
InventoryClickEvent.getRawSlot()rather thanInventoryClickEvent.getSlot()for the bounds check:getRawSlot()is an absolute index relative to the whole combined view (top GUI + player inventory) -- the top GUI occupies[0, gui.getSize())and the player's own inventory starts atgui.getSize()-- whereasgetSlot()is numbered independently relative to "whichever Inventory was clicked": the top GUI and the player inventory each count from 0 on their own, so the same value (say, 4) could be both the GUI's 4th slot and the player inventory's 4th slot. Clicking outside the window entirely (e.g. dropping an item outside it) makes Bukkit report a rawSlot of -999, which this same bounds check also rejects without throwing.clickHandlersis the sole record of this click routing -- it is populated byupdateClickHandlers(java.util.List<com.ultikits.ultitools.abstracts.gui.declarative.core.RenderNode>)usingRenderNode.getSlotIndex()(the same "slot within the top GUI" numbering space), and this method looks things up in that same space; there is no second record that could drift from it.- Parameters:
event- the click event
-
setSlot
private void setSlot(int slot, @Nullable @Nullable mc.obliviate.inventory.Icon icon) Sets a slot's content.- Parameters:
slot- the slot indexicon- the icon
-
clearSlot
private void clearSlot(int slot) Clears a slot.- Parameters:
slot- the slot index
-
dispose
public void dispose()Destroys the renderer and releases its resources. -
getGui
@NotNull public @NotNull mc.obliviate.inventory.Gui getGui()Gets the associated Gui instance.- Returns:
- the Gui instance
-
getScheduler
Gets the scheduler.- Returns:
- the GuiScheduler
-