Skip to content

Styling

“Perrier” edited this page May 23, 2026 · 2 revisions

Styling

Every component takes a Style (immutable) that controls size, spacing, color, font, border, alignment, opacity, and click handling. Styles are built with a fluent Style.Builder; static factories on Style and Styles give you a head start.

Quick reference

import org.triggersstudio.moddinglib.client.ui.styling.Style;
import org.triggersstudio.moddinglib.client.ui.styling.Styles;

Style s = Style.builder()
        .width(120).height(28)
        .padding(4, 8)
        .backgroundColor(0xFF_2A_2A_2A)
        .textColor(0xFF_FF_FF_FF)
        .border(0xFF_55_55_55, 1)
        .borderRadius(4)
        .build();

Or with the static-import helpers:

import static org.triggersstudio.moddinglib.client.ui.styling.Styles.*;

Style s = padding(4, 8)
        .width(120).height(28)
        .backgroundColor(0xFF_2A_2A_2A)
        .textColor(WHITE)
        .border(0xFF_55_55_55, 1)
        .borderRadius(4)
        .build();

Style.DEFAULT is the no-op style: WRAP_CONTENT size, white text, 9pt font, no background, no border, fully opaque.

Style.Builder

Every method returns the builder for chaining. Call .build() at the end.

Method Notes
width(int) / height(int) Accepts Size.WRAP_CONTENT (-1), Size.MATCH_PARENT (-2), or a positive pixel count.
padding(int all) Same on every side.
padding(int vertical, int horizontal) Top/bottom = vertical, left/right = horizontal.
padding(int top, int right, int bottom, int left) Per-side.
margin(...) Same overloads.
backgroundColor(int color) ARGB. 0x00_00_00_00 = transparent.
textColor(int color) ARGB. Default 0xFF_FF_FF_FF.
fontSize(float) Default 9f.
borderRadius(int) Corner rounding in pixels. Clamped to min(width, height) / 2.
border(int color, int width) Shorthand for color + width.
align(Alignment h, Alignment v) Text/content alignment.
onClick(ClickHandler) (double x, double y, int button) -> void.
opacity(float) Clamped to [0, 1].
bold() Bold text (renders via Minecraft's bold formatting).
placeholderColor(int) TextField/TextArea placeholder color. 0 = derive from textColor at reduced alpha.
font(Identifier) Render text with a Minecraft resource-pack font. See Custom fonts.
background(Paint) Solid or gradient fill for the component background. See Paint and gradients.
textFill(Paint) Solid or gradient fill applied per-glyph to text. See Paint and gradients.

Style.toBuilder() returns a builder pre-populated with the style's current values, so you can derive variants without restating everything.

Static factories on Style

Each one creates a builder with that property set, so you can drop .builder() from one-liners:

Style.width(60).height(20).build()
Style.padding(8).backgroundColor(0xFF_22_22_22).build()
Style.border(0xFF_55_55_55, 1).borderRadius(4).build()

Available: width, height, padding, margin, backgroundColor, textColor, fontSize, borderRadius, border, align, onClick, opacity, bold, placeholderColor, font, background, textFill.

Styles helper

org.triggersstudio.moddinglib.client.ui.styling.Styles re-exports the static factories plus a few constants. Import statically and you don't need the Style. prefix:

import static org.triggersstudio.moddinglib.client.ui.styling.Styles.*;

Size constants

WRAP_CONTENT   // -1
MATCH_PARENT   // -2

Alignment constants

START   CENTER   END   STRETCH

Color constants

BLACK         = 0xFF_00_00_00
WHITE         = 0xFF_FF_FF_FF
RED           = 0xFF_FF_00_00
GREEN         = 0xFF_00_FF_00
BLUE          = 0xFF_00_00_FF
TRANSPARENT   = 0x00_00_00_00

Builder shortcuts

size(int width, int height) plus the same factory list as on Style (width, height, padding, margin, backgroundColor, textColor, fontSize, borderRadius, border, align, onClick, opacity, bold, placeholderColor, font, background, textFill).

Insets

Used internally by padding(...) and margin(...). You rarely instantiate one yourself.

new Insets(int all)
new Insets(int vertical, int horizontal)
new Insets(int top, int right, int bottom, int left)

insets.getHorizontal()   // left + right
insets.getVertical()     // top + bottom

Insets.ZERO              // no inset constant

Alignment

public enum Alignment { START, CENTER, END, STRETCH }

Used for text alignment in Text and content alignment in containers when the cross-axis has free space.

Size

Layout sentinels:

Size.WRAP_CONTENT          // -1
Size.MATCH_PARENT          // -2

Size.isWrapContent(int)
Size.isMatchParent(int)
Size.isFixed(int)

Defaults

The builder defaults match Style.DEFAULT:

Property Default
width / height WRAP_CONTENT
padding / margin Insets.ZERO
backgroundColor 0x00_00_00_00 (transparent)
textColor 0xFF_FF_FF_FF (white)
fontSize 9f
borderRadius 0
borderColor 0xFF_00_00_00
borderWidth 0
horizontalAlignment Alignment.START
verticalAlignment Alignment.START
clickHandler null
opacity 1.0f
bold false
placeholderColor 0 (derive from textColor)
font null (vanilla font)
backgroundPaint null (derive from backgroundColor)
textPaint null (derive from textColor)

Paint and gradients

Paint is a sealed interface that describes how a region should be filled — either a single ARGB color or a multi-stop gradient. It plugs into both Style.background(Paint) (component backgrounds) and Style.textFill(Paint) (per-glyph text gradients).

import org.triggersstudio.moddinglib.client.ui.styling.Paint;

Variants

Variant What it does
Paint.Solid(int argb) Single ARGB color.
Paint.LinearGradient(double angleDegrees, List<Stop>) Sweep along a straight axis. = left → right, 90° = top → bottom.
Paint.RadialGradient(double cx, double cy, double rx, double ry, List<Stop>) Disc/ellipse from a fractional center point (cx, cy)[0,1]². rx, ry are radii as fractions of the rect's width / height.
Paint.ConicGradient(double cx, double cy, double startAngleDegrees, List<Stop>) 360° clockwise sweep around (cx, cy).

Stop

A color anchor along the gradient. Offset is in [0, 1]; multiple stops give multi-color gradients.

Paint.stop(double offset, int argb)

Factories

Paint.solid(int argb)
Paint.linear(double angleDeg, int from, int to)               // two-color shorthand
Paint.linear(double angleDeg, Paint.Stop... stops)             // multi-stop
Paint.radial(int from, int to)                                 // centered, full disc
Paint.radial(double cx, double cy, double rx, double ry, Paint.Stop... stops)
Paint.conic(double startAngleDeg, Paint.Stop... stops)         // centered
Paint.conic(double cx, double cy, double startAngleDeg, Paint.Stop... stops)

Stops do not need to be passed in order — Paint sorts them on construction. Two stops minimum for any gradient.

Examples

Linear, multi-stop, custom angle:

Paint sunset = Paint.linear(45,
        Paint.stop(0.0, 0xFF_FF_55_55),
        Paint.stop(0.4, 0xFF_FF_DD_55),
        Paint.stop(1.0, 0xFF_55_AA_FF));

Style style = Style.width(280).height(160)
        .background(sunset)
        .borderRadius(8)
        .build();

Radial spotlight:

Paint glow = Paint.radial(0.5, 0.5, 0.7, 0.7,
        Paint.stop(0.0, 0xFF_FF_FF_FF),
        Paint.stop(1.0, 0xFF_11_22_44));

Conic for color-wheel feel:

Paint wheel = Paint.conic(0,
        Paint.stop(0.00, 0xFF_FF_55_55),
        Paint.stop(0.25, 0xFF_FF_DD_55),
        Paint.stop(0.50, 0xFF_55_FF_88),
        Paint.stop(0.75, 0xFF_55_AA_FF),
        Paint.stop(1.00, 0xFF_FF_55_55));

Gradient text

Style.textFill(Paint) paints text glyphs with the same paint shapes. Each codepoint is drawn individually with the paint sampled at its glyph center, so multi-stop / radial / conic all work uniformly. Style.bold() is preserved on the gradient path — bold routes through MC's standard §l mechanism applied per glyph.

Style title = Style.builder()
        .fontSize(24).bold()
        .textFill(Paint.linear(0,
                Paint.stop(0.0, 0xFF_FF_55_55),
                Paint.stop(1.0, 0xFF_55_AA_FF)))
        .build();

Back-compat

Setting backgroundColor(int) or textColor(int) clears any paint already configured on that channel — last-write-wins. Conversely, calling background(Paint) keeps the legacy getBackgroundColor() getter meaningful by syncing it to the paint's first-stop color, so code that hasn't migrated to getBackgroundPaint() keeps working.

Performance

Gradients are rendered with a per-row scanline fill: each row is walked once, color is sampled at each pixel, and adjacent pixels with near-identical (5-bit-quantized) colors are coalesced into a single DrawContext.fill. The output is a few dozen fills per row at most regardless of the rect's width — comparable in cost to the rounded-corner code path. Solid fills bypass the scanline walk entirely and run as cheaply as the original fillRoundRect.

Custom fonts

Style.font(Identifier) switches text rendering to a Minecraft resource-pack font. Minecraft natively supports TTF, bitmap, and unihex providers via the resource-pack font system; the library just exposes the identifier so you can apply it from your Style.

import net.minecraft.util.Identifier;

Style title = Style.builder()
        .font(Identifier.of("yourmod", "my_font"))
        .fontSize(24)
        .build();

That Identifier resolves to assets/yourmod/font/my_font.json in your resource pack. A typical TTF font definition looks like:

{
  "providers": [
    {
      "type": "ttf",
      "file": "yourmod:my_font.ttf",
      "shift": [0, 0],
      "size": 11.0,
      "oversample": 1.0
    }
  ]
}

The TTF itself goes at assets/yourmod/font/my_font.ttf. The library does not ship any sample font — you provide the file in your own resource pack.

font(...) is honored by Components.Text. Width measurement re-runs through the styled Text so layout matches what's drawn. Combining font(...) with textFill(Paint.linear(...)) works — bold + custom font + gradient also composes correctly.

Clone this wiki locally