Tags
Creators
Details
7.4.0+26.3
Compatibility
Required content
Changes
7.4.0
This release is additions: the container region, background panels, Panel-look panels that follow resource packs, a placement per game mode, panels several mods share, and panels players can move. Nothing was removed, and mods built for 7.3.0 work as before except in the two cases Upgrading names. The forms with no game mode are deprecated. A resource pack that themed MenuKit's buttons through menukit:panel_raised themes them through menukit:control_raised now.
Added
- One scrolling column above the inventory on container screens.
ContainerRegion.section(id, scope)adds a section to the screen's container region: the chest's own grid, a box opened below it, another below that, stacked in one column above the player's inventory. There is one region per screen, however many mods add to it. The screen becomes two panels, each with its own frame: the container list, with its scrollbar inside its frame, and the inventory panel below it, which never scrolls. The list grows before it scrolls. While the two panels fit, they stay centered on the screen as one block, the list's top moving up and the inventory panel down by half the growth each (on a window too short for vanilla's own margins, the side with less room holds until the block is centered). Only once they reach the screen's top and bottom margins does the list scroll, by pixels, with the shared scrollbar. A resize lays it out again, and the list is never shorter than the screen's own container part. The screen'sleftPos,topPos,imageWidthandimageHeightare the rectangle around both panels whenever the layout changes, so panels on the menu frame, other mods' buttons and recipe viewers go around both, and a click in the gap between them with an item held drops nothing; vanilla's inventory slots and label go with the inventory panel, and a mod readingleftPos + slot.xlands on the slot.ContainerRegion.CONTAINERSandContainerRegion.INVENTORYare anchors for each panel.ContainerRegion.anchor(sectionId)is an anchor for one section, so a button can sit by each box in the list and hides when its section is hidden or scrolled out of view. The wheel scrolls the region from anywhere on the screen, but only on a turn nothing else used, so over a slot a mod that moves items with the wheel keeps it, and so does a recipe viewer's list. A shift-click into one of its slots scrolled out of view scrolls there, and carrying an item to its top or bottom edge scrolls it, faster nearer the edge. Both panels are the screen's background. Everything else on the screen, other mods' buttons and recipe viewers included, draws over them and over the region's slots, and they block none of it. While the region is up, the screen's background runs as usual, other mods' hooks in it included. Only vanilla's own container texture and the screen's title are left out, and the panels and the sections' titles take their place. Concepts, recipe. - The container region lays the screen out again when its layout changes while the screen is open, the way a window resize does (
Screen.resizeat the screen's own size). Every mod adds its widgets again from its own init hook, at the new bounds, so another mod's button by the screen's edges follows the panel it was by when a section opens or closes. It never happens on a scroll, and happens at most once a frame however many sections changed. What a resize keeps (the item on the cursor, the scroll, open sections, the anvil's name) is kept. Concepts. - A container region section shows only while its content is visible, so content that hides itself, such as a slot library's element for a slot group not on the open menu, takes its section with it.
ContainerRegion.Builder.title(...)draws a section's title where vanilla draws a container's. Where a resource pack blanks that title while other sections show, the section is called by its container, so the player can still tell the containers apart. The name is its source's custom name or its block's name ("Lime Shulker Box") in the player's language, orname(...)for a section with no block behind it. Recipe. - Tabs on the container screen's panels.
ContainerTabs.tab(id, scope)puts a tab, like the creative inventory's, on an edge of the container region's panels:edge(...)is the container list's top (the default) or bottom, or the inventory panel's top or bottom. Everything a tab is and does is the mod's, read every frame:onClick(left click, with vanilla's click sound), its icon (icon(Supplier<ItemStack>), or a sprite of the mod's own withicon(Identifier)),tooltip,order,visibleWhen,selectedWhenanddisabledWhen(grayed, no click). MenuKit keeps no selection. Every mod's tabs on one edge share one row, byorder, side by side and touching. The row is vanilla's creative tabs, so it follows a pack that restyles them, and a selected tab is open into its frame. It stands outside its panel, never over a slot, and the region makes room for it and counts it in the screen's bounds. Past what fits, arrows at the row's ends page it a tab at a time; nothing on the row takes the wheel. Where the region isn't up (the player's inventory, a shulker box, a chest with no section showing, a mod's own container screen), the top row stands on the screen's own frame's top edge and the bottom row on its bottom edge, the frame and the two rows centered as one block, the frame moving from vanilla's place with everything vanilla places from it, so screens of one size put the row in one place. On the region's panels too, the rows on the block's outside edges are centered with it. A row keeps its page on a screen opened in place of another. Concepts, recipe. - Tabs per key.
ContainerTabs.keyed(id, scope, keys)declares a list of tabs with one tab per keykeysgives, read every frame, so a mod can show a tab for every box the player has open, however many there are, without sections behind them.tab((key, tab) -> ...)sets each key's tab up the frame the key appears; it is kept while the key is there, and the row's page and a selected tab kept in view follow it. The list's tabs stand in the keys' order at the list'sorderandedge, on a screen's own frame or the region's panels. A row's page now stays on its first tab in view as tabs before it come and go. Concepts. - Tab groups. A keyed list of tabs with
header(...)is a group: a header tab and the keys' tabs after it as smaller sub-tabs on the same line. The mod decides what a click on the header does, with itsonClick, and when the sub-tabs are out, withexpandedWhen(...). MenuKit slides them out from under the header to its right and back under it, eased alike each way, and a turn mid-way goes back from where they are. A slide that makes the row page opens the arrows' room with it, and a tab shows whole or not at all. A screen's first frame shows every group as it is, without the slide. The arrows page through the tabs that are out. A header or sub-tab withpinnedWhen(...)is pinned while it holds: paged past, it stays at the end of the row it would leave by, the tabs beyond it going behind it, beside the group's other pinned tabs, until the next group's header (at the left end) or the previous group's pinned tabs (at the right end) push them off. Concepts. - Tab sizes and icons the mod draws.
size(width, height)sets a tab's size andtabSize(width, height)every tab in a keyed list. A row is as tall as its tallest tab, and the tab sprite keeps its corners whole at any size.icon(Icon)runs the mod's own drawing every frame in the tab's icon box, which shrinks with a smaller tab, so an icon can be an item, a sprite, an animation or anything else. The item and sprite forms fill the same box. - Vanilla's container screens are one size. The shulker box (167 tall) and the single chest, barrel and ender chest (168) are 166, as the inventory and nearly every other container screen are, so the frame and the player's inventory stay put when one opens in place of another. Every chest size also loses the blank row vanilla keeps under its image.
ScreenFrames.stretch(screen, seam, by)makes a mod's own screen taller or shorter at a row of its layout, to match. Concepts. - A container screen opened in place of another moves its frame from the old size and place to its own over 0.15 s instead of jumping, in the resource pack's look, with the new screen's contents showing at once inside it and the tab rows on its edge. Any mod's screens, no code needed.
- What a slot's item draws as.
SlotItemLooks.declare(id, look)lets a mod say, per slot and per frame on any container screen, whichItemAnimationa slot's item draws with and how far through it is; the count, durability bar, highlight, tooltip and clicks stay vanilla's.ItemAnimation.OPEN_LIDopens a box's lid with the openness vanilla's shulker box and chest item renderers take, in the pack's model and texture, and a mod can write its own.posed(...)scales and moves the item as it plays.Transition.of(millis, easing)times it per key, forward and holding, or back, turning mid-way from where it got to, on vanilla's clock (Transition.eachWayeases back toward 0 as forward eases toward 1, rather than the same curve backwards), so a screen opened in place of another picks a finished animation up where it stood.Easinghas four curves, and any function is one. Concepts. - Container region sections for what opens at runtime.
ContainerRegion.sections(kind, scope, keys)declares a kind of section with one section per key open on the screen, read every frame, so a mod can show as many boxes as the player opens without declaring one for each ahead of time. Each key's section is built from the key when it appears and goes when the key does; a tab per key isContainerTabs.keyedover the same keys. Concepts. - The fitted frame.
ContainerRegion.fitted(scope, screens)makes the container list and the inventory panel on those screens one frame, as vanilla's image is, as tall as the shown sections need, shorter than the screen's own container part where they are smaller. A tab row on the list's top edge stands on the frame, the sections' titles under it where vanilla's are. With tabs,visibleWhenand this, a mod shows one container at a time, the way the creative inventory shows one page. - A hidden container region section's slots are off on the client (
Slot.isActive()is false), so nothing draws, hovers or takes a click there. A container's own slot no section has drawn yet counts as the screen's own container's, its first section with no source.ContainerRegion.openedOn()gives the container the open screen was opened on, the source its own sections take.ContainerRegion.Builder.onHiddenLanding(...)runs when a shift-click lands an item in a hidden section, so its mod can show it; the region scrolls only to a slot in a shown section. - Panels behind everything.
Panel.Builder.background()makes a panel part of the screen's own background. It draws where the screen draws its image, so the screen's widgets, labels and slots, every other mod's widgets and every other panel draw over it. It claims nothing, so whatever is over it stays live, and its own elements get a click only after the screen's widgets declined it and the wheel only after everything else declined it. Slots it shows draw right after it, clipped to it, so they sit over the panel and under every widget. A button over one hides it, and over the button the slot is not hovered. Every slot goes through the screen's ownextractSlot, so other mods' slot marks show. Recipe. - Panel-look panels follow the resource pack. A raised panel in the Panel look, on a container screen, on a mod's own Panel-look screen or as a dialog, takes its frame, fill and slot frames from the pack's chest art, cut from the texture and tiled, never stretched, at any resolution. In the container region each section's part of the list takes its own container texture, so a shulker box in a chest's list looks as it does on its own screen, and the inventory panel takes the inventory half of the screen's texture. Art that can't be cut cleanly, such as a textured fill, keeps MenuKit's look, and with no pack nothing changes. Mods have nothing to do. Resource packs.
- Panel-look controls follow the pack's restyled vanilla widgets. Buttons, toggles, dropdowns, menu buttons, the color field's swatch and the item dialog's controls draw vanilla's
widget/buttonsprites where a pack restyled them, with vanilla's label colors, and checkboxes draw vanilla's checkbox. A pack can theme MenuKit directly instead withmenukit:panel_raisedand the newmenukit:control_raised,control_raised_highlighted,control_raised_pressedandcontrol_raised_disabled, which win over vanilla's. Every state of a control comes from the same source as its normal state, and a state the source has no sprite for is derived from the normal one, so a control never changes look between states. Resource packs. - Text on a Panel-look panel reads on any pack's fill. Heading, body and muted text keep their colors wherever they stand out from the fill (4.5:1 for body text) and take a color made from the fill where they don't. On a dark-mode pack whose text shader repaints vanilla's label gray, MenuKit's gray is left for the shader. A custom element gets this through
AbstractPanelElement.textColor. - A container region section can name the container it shows.
ContainerRegion.Builder.source(...)names it, withContainerRegion.Source.of(BlockEntity)for a placed one andSource.of(ItemStack)for one opened from its item (an item that isn't a block gives no block). The section takes its container texture and its name from it. A section without one is the screen's own container, the block under the crosshair as the screen opened when it holds a container of the screen's size and the screen has its title. - Per-section container art through OptiGUI. With OptiGUI 2.x installed, MenuKit evaluates the pack's OptiGUI and OptiFine rules for each container region section against its source, so a lime box below a chest has the lime art and the chest keeps its own. Without OptiGUI the rules aren't read. Recipe.
ContainerRegion.Builder.texture(...)names the container texture a section stands for where no source's block gives one, such as a mod's own slot group, eitherContainerRegion.CHEST(the default) orContainerRegion.SHULKER_BOX. A source's block wins, so a shulker box gives the shulker box's texture and a chest, ender chest, or barrel gives the chest's.ContainerRegion.GAPis the room between two sections andContainerRegion.CONTENT_WIDTHa section's width before the scrollbar.Panel.isBackground()says whether a panel was built withbackground().- A placement per game mode.
ModeScopeisSURVIVAL,CREATIVE, orBOTH, from the player's infinite materials (creative is a player with them), read every frame, so/gamemodewith a screen open takes effect on the next frame. With no player onlyBOTHapplies. It is a plain value in MenuKit's common code, so a library that judges on the server uses the same type.Panel.Builder.in(scope, placement)says where a panel goes in each mode, once per scope. Each frame the panel takes the current mode's placement, else itsBOTHone, and with neither it doesn't show in that mode.Placement.screen(position)is against the host's own reference,Placement.around(anchor, position)in the regions around an anchor's box, andPlacement.at(anchor, dx, dy)at the box's top-left plus an offset, at natural size, with no stacking, budget or clamp. A screen adapter puts a panel's anchored placements in their anchors' hosts on the screens it targets, and anAnchorPanelAdapterneeds noon(...)when the placements name their anchors.Panel.placements()lists them, andgetPosition()is the position in effect now.HudPanel.builder(id, scope),OwnHost.add(panel, padding)andContainerRegion.section(id, scope)are the scoped forms.inand the deprecatedpositiondon't mix on one panel, each scope is declared once, and two panels that would both show amain()in survival or in creative (each by its placement for that mode, else itsBOTHone) throw at registration, naming both panels and the mode. AnAnchorPanelAdapterwhose panel has aPlacement.screen(...)and noon(...)throws at the first screen open, naming the panel and the mode, instead of leaving it out in that mode.Panel.Builder.fixed()marks a panel the player can't move, andPanel.isFixed()reads it back. Concepts, recipe. OutsideRegion.LEFT_CENTERandRIGHT_CENTERsit beside the reference, centered on it vertically, asTOP_CENTERandBOTTOM_CENTERare above and below it, so a column beside an anchor's box (the armor slots, say) sits level with the box's middle. Several stack straight away from the reference, each centered. Every side now has an aligned-to-start, a centered, and an aligned-to-end region. Aswitchexpression overOutsideRegionwith nodefaultneeds cases for them (Upgrading).- For slot libraries,
SlotClip.clipOf(slot)gives the rectangle a slot is shown within, in screen space, ornullwhen nothing clips it, so a library can tell a slot cut by a scroll edge from one wholly in view, for instance to hide a panel anchored on a cut slot. PanelElement.renderCulled(ctx), called each frame instead ofrenderfor a shown element skipped only because it lies wholly outside the clip, such as a row scrolled out of aScrollContaineror aListView, or a container region section out of view. A slot element can then keep its slot parked while it is out of view, instead of sending a vanilla slot back to the menu's own place, where it would sit over other sections. A hidden element gets neither call.Flow,ScrollContainer,Section(while open),Tabs(its headers and shown page) andListViewforward it to their shown children, andChildDispatch.renderCulleddoes the same for a custom container. Concepts.- Shared panels. Mods that work together put their elements in one panel. Each joins it from its client initializer with
SharedPanels.join(id, contribution), under an agreed id that is a plain string. AContributioncarries its elements, an order number, its mode scope, and its ownvisibleWhen, along with the panel's own declaration (placement per mode, style, axis, and gap). MenuKit builds the panel once at client start, with the contributions in one flow by order, then mod id. A hidden or out-of-scope one, or one whose elements are all hidden, takes no room, and with all of them hidden the panel isn't placed. Where contributions disagree on the declaration, the lowest order's wins and a warning names both mods. A shared panel with a screen placement shows on every container screen. With one mod installed it is an ordinary panel. Concepts, recipe. - Players move panels. A "Move panels" key, unbound by default, turns edit mode on for the open screen, or opens a clear screen over the HUD in game. While editing, panels have outlines and nothing else takes a click. Drag one to move it, for both game modes or with Shift for the current one, right-click to put it back, and Esc or the key to finish. A position is kept from the nearest third of the screen on each axis, so it survives a resize and a GUI scale change, and is stored by panel id in
config/menukit/panel-positions.json. An entry MenuKit can't read is skipped and the rest kept. A file that isn't JSON at all is kept beside it aspanel-positions.json.corrupt. A file that can't be read at all (locked, or without permission) is left as it is, and moves aren't saved until the next start. A moved panel shows only where its declaration would, and leaves its region's flow. Edit mode ends with its screen, dropping a drag in progress unsaved, and the key does nothing while a text field has the keyboard or the Controls screen waits for a binding.Panel.Builder.fixed(),HudPanel.Builder.fixed()andContribution.Builder.fixed()keep a panel where its mod put it. Background panels, overlays, popovers, and a screen's main panel are never movable, and the cycle strip is fixed. Concepts. - A grid of slots that scrolls inside its panel when it doesn't fit.
SlotScroll.builder()makes one from a slot library's elements, one per cell, each made from its index and place (cells(count, cell)). It is as tall as its live rows while there is room and gives back whole rows once there isn't, never underminRows(3), then scrolls. As a container region section it shows every row while the window has the height and scrolls once it is too short.narrows()makes it a column fewer a row while it scrolls, the scrollbar in the freed column, for a bundle that must not widen its panel; otherwise the scrollbar's lane is beside it and the frame widens.liveCount(...)ends it at the last live slot, read once a frame. A shift-click into a row scrolled out of view scrolls there. Concepts, recipe. - A vanilla screen's own grid scrolls when its frame doesn't fit, where a mod turns it on.
ScreenFrames.scrollsWhenShort(scope, screens)takes the screens it names (the large chest, say): while one's frame and its tab rows are taller than the window less 4 pixels at the top and bottom, its own container grid scrolls inside a frame that fits, and the title, the inventory and the hotbar stay put. On a window the frame fits, the screen is vanilla's to the pixel, and with no declaration no grid scrolls (the standard 166 pixel height and the moving frame stay on either way). The grid's slots are vanilla's own, each shown one where it is drawn, so recipe viewers and mods readingslot.x/yfind it there. - A scroll area that fits the room.
ScrollContainer.Builder.fitRoom(step)makes a scroll area as tall as its content while there is room and gives height back in whole steps, never below its declared height, once its container asks: in a container region section it shows every row while the window has the height, and the list itself scrolls only once such grids are at their least.laneOutside()puts the scrollbar's lane beside the declared width, taken only while it scrolls, and the region widens its frame by as much. ScrollContainer.scrolls()says whether a scroll area scrolls now, its content taller than its viewport, andContainerRegion.scrolls(screen)whether the container list on a screen, or a section's own grid in it, does: where either is true, the wheel over it is the scroll's, so a mod that switches tabs with the wheel leaves it alone there.
Changed
- The container region is never on the player's own inventory, survival's or creative's, whatever a section's targeting says. The player's inventory slots it moves are placed every frame from the slots the menu holds, so a slot another mod puts in while the screen is open goes with the inventory panel too, and closing the screen moves back exactly the slots that were moved.
- A container region section's content is laid out to the section's width, nine slots (
ContainerRegion.CONTENT_WIDTH), so content that wraps (a horizontalFlow, a slot library's wrapping row of slots, a long label) wraps there instead of running on in one row. SlotClip.panelShowing(slot)keeps the panel that last showed a slot by the slot's container, index and class, so a slot made again for the same place, such as creative's inventory slots on a tab switch, keeps its answer. It is forgotten when another container screen opens, so the player's inventory slots, which are the same slots on every screen, don't carry one screen's panel to the next.- A click on one of a container screen's widgets is never eaten as a click in a slot panel's gap or on the hidden part of a slot cut by a scroll edge. The widget gets it.
- An element wholly outside the clip it is drawn in is skipped at every depth, not only a
ScrollContainer's own children. A row of slots in aFlowinside a scroll area is not drawn while it is out of view, so its slot library parks it and nothing scrolled away sits over what is past the edge, such as the inventory below a scrolling grid. An element with an open popover is still drawn. A custom element that draws outside its own box is not drawn while its box is wholly out of the clip, overhang included (Upgrading). - A slot two panels show in one frame belongs to the panel on top, the one that takes clicks over both, in everything. It draws after that panel, at its place and under its clip. Before, it drew with the lower panel while its hover, its clip and
SlotClip.panelShowingfollowed the upper one. MenuKit logs the clash once for each pair of panels. - Raised controls in the Panel look draw MenuKit's new
menukit:control_raisedsprites instead ofmenukit:panel_raised, so a pack themes panels and controls each on its own. A pack that overrodepanel_raisedto restyle MenuKit's buttons now overridescontrol_raised. With no pack they look as before. - The pressed look on a held button, MenuKit's own and vanilla's, is one transform drawn relative to the button's art, so it reads on a pack's dark or colored buttons as well as on vanilla's. On vanilla's own button it matches the old pressed look to within a few levels along the bevel.
SlotRendering.drawSlotBackgrounddraws an 18 by 18 slot on a Panel-look panel as the pack's slot, cut from its container art or its restyledcontainer/slot. Elsewhere it draws as before.
Deprecated
These keep working, mean ModeScope.BOTH, and are removed at 8.0.0. Upgrading.
Panel.Builder.position(position)is replaced byin(ModeScope.BOTH, Placement.screen(position)).AnchorPanelAdapter.on(anchors)is replaced by the anchor in the placement,Placement.around(anchor, position).HudPanel.builder(id)is replaced byHudPanel.builder(id, scope).OwnHost.add(panel, position, padding)is replaced byOwnHost.add(panel, padding), which reads the panel's placements.
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:3vGajyPr:YlmYrzVP"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:3vGajyPr:YlmYrzVP"
}

