Tags
Creators
Details
6.1.0+26.2
Compatibility
Required content
Changes
6.1.0
Screen kinds and the pause-menu look. Every element now draws for the kind of screen it is on: MenuKit's grey look on container screens (inventories, chests), and vanilla's pause-menu look on options screens (Options, Video Settings, Controls, Create World). A settings page can let the player pick items with the new item picker. A column of buttons can wrap into more columns where the screen's frame ends, and a panel can pop up over every other panel on the screen.
Added
ScreenKind(CONTAINER,OPTIONS) and one rule for where it comes from,ScreenKind.of(screen). A vanillaAbstractContainerScreenis a container screen, and any other vanilla screen is an options screen. A standaloneMKScreenis the kind it declares when it is made (super(title, ScreenKind.OPTIONS, panels), read back withscreenKind()), and one that declares none is a container screen, as before. Slot-group panels and the HUD are container. A dialog, or any panel on a screen, takes that screen's kind. The kind travels inRenderContext.kind(), so every element follows it, inside sections, scroll areas, tabs and rows, and inside elements built from other elements (Keybindery's key button).- Text colour by role:
TextRole(HEADING,BODY,MUTED,DISABLED),TextLabel.Builder.role(...), andScreenKind.textColor(role)andtextShadow(). Text is dark grey on a container screen, and vanilla's white and greys with a shadow on an options screen. A label with no role isBODY, which on a container screen is the colour it already was. Checkbox, radio and section header labels follow the same table.ElementConstants.TEXT_MUTEDnames MenuKit's muted grey. - On an options screen, the controls use vanilla's sprites.
Buttonand the dropdowns are vanilla's buttons,Checkboxis vanilla's checkbox, andToggleis a vanilla button whose text is its state (its label, or vanilla's ON and OFF). ASectionheader uses the Controls screen's category colours, but reads from the left, in line with its content, rather than centred as vanilla's is. ADivideris vanilla's separator. A panel'sRAISEDbackground is vanilla's popup frame, so a dialog looks like one of vanilla's own. ScrollContaineron an options screen is the band every Options screen has: vanilla's darker list background, a separator above and below, and vanilla's thin list scroller. Its content is inset a little wider than vanilla's lists,ScrollContainer.OPTIONS_INSET_X(14) from the left with the scrollbar lane on the right, andOPTIONS_INSET_Y(10) above the first child and below the last, scrolling with the content. Size content for it with the newScrollContainer.viewportWidthFor(width, kind). A panel that auto-scrolls on an options screen lays its elements out inside the inset itself.TabsinWRAPandSIDE_SCROLLon an options screen is Create World's tab bar: white labels, the selected one underlined and opening into a page of the list background, and the separator on either side. The page insets its body the same way,OPTIONS_INSET_Xfrom both sides (the scrollbar lane on the right when it scrolls) andOPTIONS_INSET_Yabove and below, so a spacer row, a full-width divider and wrapped text stay inside the margin.SIDEBARon an options screen is that tab bar turned on its side. Vanilla's tab sprites are turned a quarter turn and open toward the body on the right, and the selected one opens into a page of the list background beside the column. Vanilla's separator runs down the column's edge above and below the tabs, and the labels stay upright and white, with the selected one underlined. Its page insets the body as the top strip's does. On a container screen it keeps MenuKit's drawing.InputContext.kind()gives the kind of screen, asRenderContext.kind()carries it, so an element answers input with the geometry it drew. The five-argument constructor stays and means a container screen.Tabs.Builder.bodyScroll(Function<String, Double> get, BiConsumer<String, Double> set)keeps each tab's body scroll as consumer state, in pixels from the top and keyed by tab id, the wayScrollContainer.stateholds a container's. A screen rebuilt on the same lens opens each body where it was on the first frame, with no slide from the top. Without it, the scroll belongs to the element, as before.MKTooltip.avoidCoverWhen(BooleanSupplier). While it holds, a tooltip goes on the side of the cursor where it covers less of the screen (a container screen's frame with an open recipe book, shown MenuKit panels, the screen's widgets), with its text wrapped to the room on that side. A side is ruled out only when the tooltip cannot wrap to fit it, that is, when the side is narrower than the tooltip's longest word, an image in it, orMKTooltip.MIN_WRAP_WIDTH(60). Otherwise the side that covers less wins, however narrow. A tie goes to vanilla's side, and with no side left the tooltip is placed as vanilla places it. It is off unless a mod registers it, likewrapWhenandclampWhen.ItemPicker, a setting whose value is an item: a slot with a lens onto anItem(state(get, set),Items.AIRfor none). A click opens a chooser over the screen, of the same kind, with every item in a scrolling grid, a search field, and Clear and Cancel. A right click empties it. It is client-only and never touches the inventory or the server, because the item is a setting value, not the player's item. It draws as vanilla's slot on an options screen and as MenuKit's on a container screen.
The kind changes how an element draws, never its size or behaviour, with one exception: the options list band (a ScrollContainer, a Tabs page) insets its content. Slider and TextField are vanilla's own widgets and look the same on both. Where vanilla has no equivalent (radio buttons, info boxes, progress bars, colour swatches, consumer sprites), MenuKit's drawing stays.
Flow.builder().vertical(): a column that fills top to bottom and wraps into a new column to the right. It wraps at the frame band, the room from its top down to the bottom of the screen's frame (topPos + imageHeighton a container screen). A panel in a side region (LEFT_ALIGN_*,RIGHT_ALIGN_*) gets the band from its host on a container screen, on a slot group (where the band runs to the container screen's frame, not the group's box) and beside a standalone screen's main panel. The panel widens with each column, so aRIGHT_*region still places it beside the frame. A hidden child takes no room, so the column reflows when one hides. In a column,gap(horizontal, vertical)sets the space between columns and between children. Anywhere else the Flow is one column. Elements receive the band through the newPanelElement.wrapHeight(int)(default no-op). It is not a scroll ceiling, so nothing else about a side panel's size changes.Panel.Builder.popover()andPanel.isPopover()put a panel in the new POPOVER layer, between the ordinary (FLOW) panels and the modal OVERLAY. A popover draws above every ordinary panel of every host on the screen, whichever registered first and whichever host holds either (an adapter's popover over a slot-group panel), and below a modal and its dim. Claims and pointer input follow the same order, so a click, the wheel, hover or a tooltip over a popover never reaches a panel drawn under it. On a container screen, popovers draw after the ordinary panels and before the created slots, so a popover's own slots draw on it, while another panel's created slot under a popover still draws over it.
Changed
- One draw order across hosts now decides both what draws on top and what takes the pointer: layer by layer (FLOW, POPOVER, OVERLAY), host by host, then each host's sorted order. Claims already worked this way. Element dispatch for a point no panel claims (a see-through panel's controls) now does too. It used to walk each host's panels in turn, upper host first, whatever their layer, so an upper host's ordinary panel was offered a click before a lower host's centred overlay.
- Z-order across layers. Within a layer, z-order is still sorted order. Across layers it is FLOW below POPOVER below OVERLAY, which overlays already did without it being written down. Put fully, z-order is the layer plan's order: layers bottom to top, hosts bottom to top within a layer, and sorted order within a host.
- Panels injected into a vanilla screen that is not a container (the pause screen, Options, Controls, a mod's config screen) now draw in vanilla's look there. They used to draw MenuKit's grey.
Tabsstrips scroll the way aScrollContainerdoes, both on one scroller: by pixels, 10 per wheel notch. The wheel used to move the sidebar and the side-scroll row a whole tab at a time. An arrow, or a selected tab partly off the strip, now slides the strip there instead of jumping.- The
SIDEBAR's scrollbar is aScrollContainer's, with the same fixed 15 px handle and track in the same look per kind of screen (MenuKit's inset track on a container screen, vanilla's thin list scroller on an options screen). Its handle used to be sized to the share of the column in view, with a 32 px floor, so a column a tab or two too tall had a handle nearly as long as the track. On a container screen its lane is the wider track's. - Pressing a
ScrollContainer's track, off the handle, jumps the handle there and holds it, as the sidebar's track did and as vanilla's lists and the creative inventory do. It used to do nothing. - A
ScrollContainerwith nocontentHeightreads its children's extent every frame, so its range follows a child that grows or shrinks while shown.
Deprecated
Tabs.SIDEBAR_SCROLLBAR_WIDTH. The sidebar's scrollbar is now as wide as aScrollContainer's on that kind of screen (ScrollContainer.TRACK_WIDTHon a container screen, 6 on an options screen). It goes in 7.0.0.ControlStyle, andstyle(ControlStyle)onButton,DropdownandDropdownMulti. They do nothing now, since only the kind of screen picks a look, and they go in 7.0.0. A mod built against 6.0.0 still loads. Delete the call. On an options screen the element already draws in vanilla's look.
Fixed
- A horizontal
Flowwith a spacer no longer wraps one gap early. The row spent a gap beside a spacer that its natural width never counted, so a row given exactly its natural width (aTOP_ALIGN_RIGHTtoolbar of a spacer and three buttons) wrapped its last control onto a second row and grew taller. The gap now sits only between two pieces of content, in both the measure and the layout, and a spacer adds just its share of the leftover space. Rows with room to spare look the same as before. - A
Tabsbody or a panel that scrolled, then fit (a section closed), then scrolled again (the section reopened) jumped back to its old offset instead of starting from the top. A body that fits now resets its offset to the top. - Input to a
Tabsbody before its first draw (on a screen just rebuilt) went nowhere. The body is now laid out for input the same way as for drawing. - A dialog or a panel laid out with
RoworColumncan be built at mod init, where adapters are declared.RowandColumnused to measure their elements inbuild(), and a label's or button's size needs the game's font, which does not exist yet at init. SoConfirmDialogandAlertDialogcould only be built after the declaration freeze had closed. They now place their elements on the first read of any element's position, which in practice is the panel's first layout pass. ARoworColumnholding a customElementSpecstill lays out inbuild(), and a spacer withoutwidth(px)still throws there.
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:9MVkZ8fx"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:3vGajyPr:9MVkZ8fx"
}

