UI theme + region + widgets
Three small classes that turn screen content from “UV math by hand” into “compose named building blocks”:
Region— an immutable rectangle in UV space[0, 1] × [0, 1]with fluent helpers (inset,top,bottom,left,right,belowOf).Theme— a colour palette and scale set (bgColor,panelColor,titleScale,bodyScale, …) with presets (darkBlue(),amongUs()).Widgets— high-level helpers that push rawRect/Text/Buttonelements onto aScreenContent.Builderusing aThemeand aRegion.
When to use it
Section titled “When to use it”- You’re building any screen and want one place to look up “what’s the title colour again”.
- You want to compose a layout from regions like
screen.inset(0.02f).top(0.10f)instead of magic UV numbers. - You want consistent typography across every panel in your mod.
package me.zlex.conduit.screen.ui;
public record Region(float x, float y, float w, float h) { public static final Region FULL; public Region inset(float pad); public Region inset(float padX, float padY); public Region top(float height); public Region bottom(float height); public Region left(float width); public Region right(float width); public Region belowOf(Region other, float gap); public float right(); public float bottom(); public float centerX(); public float centerY(); public static final Codec<Region> CODEC;}
public final class Theme { public final int bgColor, panelColor, rowColor; public final int titleColor, textColor, subtleColor, dividerColor; public final int buttonBg, buttonText; public final int primaryColor, dangerColor, accentColor; public final float titleScale, headerScale, bodyScale, captionScale, padding;
public static Theme darkBlue(); public static Theme amongUs();}
public final class Widgets { public static void panel(ScreenContent.Builder b, Theme t, Region r); public static void divider(ScreenContent.Builder b, Theme t, Region r); public static void title(ScreenContent.Builder b, Theme t, float x, float y, String text); public static void sectionHeader(ScreenContent.Builder b, Theme t, float x, float y, String text); public static void label(ScreenContent.Builder b, Theme t, float x, float y, String text); public static void caption(ScreenContent.Builder b, Theme t, float x, float y, String text); public static void primaryAction(ScreenContent.Builder b, Theme t, Region r, String id, String label); public static void secondaryAction(ScreenContent.Builder b, Theme t, Region r, String id, String label); public static void dangerAction(ScreenContent.Builder b, Theme t, Region r, String id, String label); public static void titleBar(ScreenContent.Builder b, Theme t, Region r, String title, String closeButtonId);}Example
Section titled “Example”import me.zlex.conduit.screen.ScreenContent;import me.zlex.conduit.screen.ui.Region;import me.zlex.conduit.screen.ui.Theme;import me.zlex.conduit.screen.ui.Widgets;
public final class MyConfigScreen {
public static ScreenContent build(int lockCount) { Theme theme = Theme.darkBlue(); Region screen = Region.FULL.inset(0.02f); Region header = screen.top(0.10f); Region body = screen.belowOf(header, 0.01f);
ScreenContent.Builder b = ScreenContent.builder().background(0xD0000000); Widgets.panel(b, theme, screen); Widgets.titleBar(b, theme, header, "CONFIG", "close"); Widgets.sectionHeader(b, theme, body.x() + 0.02f, body.y() + 0.02f, "Locks per round"); Widgets.label(b, theme, body.x() + 0.02f, body.y() + 0.08f, Integer.toString(lockCount)); Widgets.primaryAction(b, theme, new Region(0.34f, 0.84f, 0.32f, 0.10f), "lock-plus", "+1"); return b.build(); }}Regionis a value type — every helper returns a new region. Chain them, don’t mutate.Themehas its own scales for title / header / body / caption text. UsingWidgets.titleinstead of rawTextElementkeeps every panel typographically aligned.- The
Widgetshelpers all take a builder so you stay in your own builder chain instead of re-entering it for each call.