## 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 vanilla `AbstractContainerScreen` is a container screen, and any other vanilla screen is an options screen. A standalone `MKScreen` is the kind it declares when it is made (`super(title, ScreenKind.OPTIONS, panels)`, read back with `screenKind()`), 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 in `RenderContext.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(...)`, and `ScreenKind.textColor(role)` and `textShadow()`. 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 is `BODY`, which on a container screen is the colour it already was. Checkbox, radio and section header labels follow the same table. `ElementConstants.TEXT_MUTED` names MenuKit's muted grey.
- On an options screen, the controls use vanilla's sprites. `Button` and the dropdowns are vanilla's buttons, `Checkbox` is vanilla's checkbox, and `Toggle` is a vanilla button whose text is its state (its label, or vanilla's ON and OFF). A `Section` header uses the Controls screen's category colours, but reads from the left, in line with its content, rather than centred as vanilla's is. A `Divider` is vanilla's separator. A panel's `RAISED` background is vanilla's popup frame, so a dialog looks like one of vanilla's own.
- `ScrollContainer` on 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, and `OPTIONS_INSET_Y` (10) above the first child and below the last, scrolling with the content. Size content for it with the new `ScrollContainer.viewportWidthFor(width, kind)`. A panel that auto-scrolls on an options screen lays its elements out inside the inset itself.
- `Tabs` in `WRAP` and `SIDE_SCROLL` on 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_X` from both sides (the scrollbar lane on the right when it scrolls) and `OPTIONS_INSET_Y` above and below, so a spacer row, a full-width divider and wrapped text stay inside the margin. `SIDEBAR` on 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, as `RenderContext.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 way `ScrollContainer.state` holds 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, or `MKTooltip.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, like `wrapWhen` and `clampWhen`.
- `ItemPicker`, a setting whose value is an item: a slot with a lens onto an `Item` (`state(get, set)`, `Items.AIR` for 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 + imageHeight` on 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 a `RIGHT_*` 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 new `PanelElement.wrapHeight(int)` (default no-op). It is not a scroll ceiling, so nothing else about a side panel's size changes.
- `Panel.Builder.popover()` and `Panel.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.
- `Tabs` strips scroll the way a `ScrollContainer` does, 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 a `ScrollContainer`'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 `ScrollContainer` with no `contentHeight` reads 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 a `ScrollContainer`'s on that kind of screen (`ScrollContainer.TRACK_WIDTH` on a container screen, 6 on an options screen). It goes in 7.0.0.
- `ControlStyle`, and `style(ControlStyle)` on `Button`, `Dropdown` and `DropdownMulti`. 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 `Flow` with 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 (a `TOP_ALIGN_RIGHT` toolbar 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 `Tabs` body 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 `Tabs` body 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 `Row` or `Column` can be built at mod init, where adapters are declared. `Row` and `Column` used to measure their elements in `build()`, and a label's or button's size needs the game's font, which does not exist yet at init. So `ConfirmDialog` and `AlertDialog` could 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. A `Row` or `Column` holding a custom `ElementSpec` still lays out in `build()`, and a spacer without `width(px)` still throws there.
Developer information
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle.
maven { name = "Modrinth"; url = "https://api.modrinth.com/maven" }