Skip to content

Rubric Frontends

Oliver Yasuna edited this page Jul 1, 2026 · 3 revisions

Rubric — Frontends

Rubric delegates its settings screen to whichever config-GUI library the user has installed. This is the whole "Rubric is not a GUI library" thing in practice.

Supported frontends

  • YetAnotherConfigLib (YACL) — modern, sleek, YACL-native controllers. Client-side only. Recommended default.
  • Cloth Config — older, feature-rich, better nested-category support, has a proper Requirement API for conditionally-disabled entries. Client-side only.

Install one or both. Rubric picks a provider at screen-open time based on gui.preferredFrontend in Rubric's own config:

  • AUTO — YACL first, Cloth second, whichever is installed. The default.
  • YACL — force YACL. Falls back to Cloth if YACL isn't installed.
  • CLOTH — force Cloth. Falls back to YACL if Cloth isn't installed.

Neither installed → the placeholder screen (see below).

No-frontend placeholder

If neither library is installed but the user clicks a Rubric-backed config button in ModMenu / Catalogue, they get a vanilla NoFrontendScreen that:

  • Explains no frontend is installed and suggests YACL.
  • Shows the on-disk path of the config file.
  • Has an "Open Folder" and "Open Config" button (via Util.getPlatform().openFile(...) — cross-platform).
  • Has a "Done" button to return.

The intent: even without a GUI library installed, users can still find and hand-edit the file.

ModMenu / Catalogue integration

Rubric ships ModMenuIntegration (implements ModMenuApi). Two hookups:

  • getModConfigScreenFactory() — surfaces Rubric's own config under the rubric mod entry.
  • getProvidedConfigScreenFactories() — surfaces every consumer's config under its own mod entry. Keyed by @Config#id, which must match the consumer's fabric.mod.json#id for ModMenu to attach the factory.

For Catalogue: same code path — Catalogue on Fabric reads the ModMenu API, so nothing extra is needed if both mods are present.

Multiple configs per mod

If your mod owns more than one Rubric config, register each via Rubric.register(...). The ModMenuIntegration groups them by mod ID and surfaces a ConfigChooserScreen when there's more than one — a vanilla-widget list of buttons, one per config.

To register an extra config under the same mod ID as a different @Config#id:

// Directly on the GUI hub — bypasses schema-id inference.
RubricGui.registerScreen("mymod", secondManager);

Late registration

ModMenu queries getProvidedConfigScreenFactories() lazily on every mod-list open, so managers registered after game start still appear. getModConfigScreenFactory() is queried once at mod-discovery time, but Rubric returns a factory that looks up the registry on click — same lazy semantics. Registration order doesn't matter for either.

The ScreenProvider SPI

Adding a new frontend is a plugin — no changes to Rubric proper.

package com.oliveryasuna.mc.rubric.fabric.gui;

public interface ScreenProvider {
    String id();  // lowercase, e.g. "cloth", "yacl"
    Screen create(Minecraft client, Screen parent, ConfigManager<?> manager);
}

Implementation:

  • Return null from create(...) to defer to the next provider (Rubric tries the preferred first, then any others in registration order, then falls back to the NoFrontendScreen).
  • Provide a stable id(). It's what Frontend enum values and gui.preferredFrontend reference.

Register on the client:

RubricGui.registerProvider(new MyScreenProvider());

Look at YaclScreenProvider and ClothScreenProvider for reference. Common helpers live in ScreenProviders (static utilities) and ScreenBuildContext (per-screen state).

Anatomy of the current adapters

Both providers walk the Schema tree and emit widgets. Shared helpers:

  • ScreenProviders.displayName(entry, meta, showSuffixes) — entry label with optional [restart, server] tag suffixes.
  • ScreenProviders.findRange(meta) / findOneOf(meta) — locate constraint validators.
  • ScreenProviders.useSlider(meta, range) — the heuristic for slider vs. field widget.
  • ScreenProviders.parseColor(hex, fallback) / formatColor(rgb) — hex ↔ int for @Widget(COLOR).

ScreenBuildContext holds the staged edits map + the ConfigManager + snapshot of the GUI flags. On save the context does manager.set(path, value) per staged entry then manager.save() once.

Feature parity

Not every Rubric annotation maps to a widget in every frontend. See Frontend comparison for the matrix and where the gaps are.

What next

Clone this wiki locally