Skip to content

CatFrame UI Components

dfdvdsf edited this page May 28, 2026 · 5 revisions

These are the UI building blocks provided by CatFrame — the library mod that CreateWorldUI (6.0.0+) depends on. If you're writing a custom tab or any screen that needs cycling buttons, panel rendering, or the tab system, this is your go-to reference.


GuiCyclableButton<T>

Package: decok.dfcdvadstf.catframe.ui.GuiCyclableButton

A generic cyclable button — similar to modern Minecraft's CyclingButtonWidget<T>. Click it or scroll your mouse wheel to cycle through a typed list of values. Left-click or scroll down goes forward; scroll up goes backward.

How it works

You build one with a Builder — give it a value-to-text function, a list of values, and an update callback. The button handles all the cycling internally; you just react to value changes.

Key pieces:

  • ValueToText<T> — converts your value to the display string. Gets called on every text refresh.
  • Values<T> — provides the list of values to cycle through (supports dynamic lists too).
  • UpdateCallback<T> — fires when the value changes: (button, newValue) -> { ... }
  • getValue() / setValue(T) — read or programmatically set the current value.

Example

import decok.dfcdvadstf.catframe.ui.GuiCyclableButton;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.client.resources.I18n;

// A button that cycles through difficulties
GuiCyclableButton<EnumDifficulty> diffButton = GuiCyclableButton.<EnumDifficulty>builder(
        d -> I18n.format("options.difficulty") + ": " + I18n.format(d.getDifficultyResourceKey()))
    .values(EnumDifficulty.values())
    .initially(EnumDifficulty.NORMAL)
    .build(200, width / 2 - 104, height / 2, 208, 20, (button, difficulty) -> {
        // React to the new value — no manual index juggling needed
        yourDifficultySetter(difficulty);
    });
addButton(diffButton);

Need a label prefix? Pass it as the String label argument in build() — the display becomes "label: valueText" automatically:

// With a label — display shows "Difficulty: Easy" etc.
.build(200, x, y, w, h, I18n.format("options.difficulty"), callback);

// Without a label — your ValueToText controls the full string
.build(200, x, y, w, h, callback);

Boolean on/off toggle? There's a shortcut:

GuiCyclableButton<Boolean> cheatsBtn = GuiCyclableButton.onOffBuilder()
    .initially(false)
    .build(201, x, y, 208, 20, (button, value) -> setAllowCheats(value));

Tip: Call button.updateText() in your drawScreen() if the display depends on external state. And use setValue(T) to sync the button when something else changes the underlying value.


ContentPanelRenderer

Package: decok.dfcdvadstf.catframe.ui.ContentPanelRenderer

A shared renderer for content panels — gives you the header separator, a tiled panel background, and the footer separator. Use the pieces you want individually, or let drawContentPanel() do the whole thing in one shot.

What you get

Method What it draws
drawContentPanel(x, top, width, bottom) The whole thing — header line, tiled background, footer line.
drawHeaderSeparator(x, y, width) Just the top separator line (2px tall, tiled).
drawFooterSeparator(x, y, width) Just the bottom separator line (2px tall, tiled).
drawSeparator(x, y, width, texture) A separator line with your own 32×2 texture.
drawPanelBackground(x, y, width, height) Just the panel background (tiles the 16×16 texture).

Textures

Field Path Size
HEADER_SEPARATOR createworldui:textures/gui/header_separator.png 32×2
FOOTER_SEPARATOR createworldui:textures/gui/footer_separator.png 32×2
PANEL_BACKGROUND createworldui:textures/gui/panel_background.png 16×16

You can override these in your resource pack — or pass a custom ResourceLocation to drawSeparator() if you want a different style.

Quick Example

import decok.dfcdvadstf.catframe.ui.ContentPanelRenderer;

// Draw a full content panel in one call
ContentPanelRenderer.drawContentPanel(panelX, panelTop, panelWidth, panelBottom);

// Or draw pieces separately
ContentPanelRenderer.drawHeaderSeparator(panelX, panelTop, panelWidth);
ContentPanelRenderer.drawPanelBackground(panelX, panelTop + 2, panelWidth, panelHeight - 4);
ContentPanelRenderer.drawFooterSeparator(panelX, panelBottom - 2, panelWidth);

Note: The separator height is always 2 GUI pixels (SEPARATOR_HEIGHT = 2). When using drawContentPanel(), the background is automatically placed between the two separators.


Tab System

The tab system from CatFrame provides a clean way to build multi-tab screens. Each tab implements the Tab interface, and you can extend AbstractScreenTab for convenience.

Tab Interface

Package: decok.dfcdvadstf.catframe.ui.tab.Tab

The core contract for a tab:

public interface Tab {
    void initGui(TabManager tabManager, int width, int height);
    void drawScreen(int mouseX, int mouseY, float partialTicks);
    void actionPerformed(GuiButton button);
    void mouseClicked(int mouseX, int mouseY, int mouseButton);
    void keyTyped(char typedChar, int keyCode);
    int getTabId();
    String getTabName();
    void setVisible(boolean visible);

    /** Default: {@code catframe:textures/gui/tabs.png} */
    default ResourceLocation getTabTexture();
}

AbstractScreenTab

Package: decok.dfcdvadstf.catframe.ui.tab.AbstractScreenTab

A base implementation that handles the boring stuff — button visibility, localization, button management, and tab texture. Extend this for your custom tabs.

import decok.dfcdvadstf.catframe.ui.tab.AbstractScreenTab;
import decok.dfcdvadstf.catframe.ui.tab.TabManager;

public class MyCustomTab extends AbstractScreenTab {

    public MyCustomTab() {
        super(103, "mymod.tab.custom"); // Tab ID and localization key
    }

    @Override
    public void initGui(TabManager tabManager, int width, int height) {
        super.initGui(tabManager, width, height);
        // Add your buttons and fields here
        setVisible(false); // Always hide by default — TabManager will show the active one
    }

    @Override
    public void drawScreen(int mouseX, int mouseY, float partialTicks) {
        if (!visible) return;
        // Draw your tab content here
    }

    @Override
    public void actionPerformed(GuiButton button) {
        // Handle button clicks here
    }

    @Override
    public void mouseClicked(int mouseX, int mouseY, int mouseButton) {
        // Handle mouse clicks here
    }

    @Override
    public void keyTyped(char typedChar, int keyCode) {
        // Handle key input here
    }

    // Optional: custom tab texture (default is tabs.png)
    @Override
    public ResourceLocation getTabTexture() {
        // Return a custom texture, or rely on the default
        return new ResourceLocation("mymod", "textures/gui/my_tabs.png");
    }
}

Tip: Call setTabTexture(ResourceLocation) from the constructor or initGui() if you prefer a setter over overriding getTabTexture(). The default texture is catframe:textures/gui/tabs.png.

TabManager

Package: decok.dfcdvadstf.catframe.ui.tab.TabManager

TabManager handles tab switching, state persistence (survives window resizes), and gives you access to the parent screen.

Method What it does
addButton(GuiButton) Add a button to the screen's button list.
registerTab(Tab) Register a tab instance (internal — use TabRegistry instead).
switchToTab(int tabId) Switch to a tab by ID.
drawScreen(...) Delegate drawing to the current tab.
actionPerformed(GuiButton) Delegate button clicks to the current tab.
mouseClicked(...) Delegate mouse clicks to the current tab.
keyTyped(...) Delegate keyboard input to the current tab.
reinitializeTabs(width, height) Re-init all tabs on window resize (preserves current tab).
getScreen() Get the parent GuiScreen instance.
getCurrentTabId() Get the currently active tab ID.
getTabCount() Get the total number of registered tabs.
getTabBar() Get the optional TabBar associated with this manager, or null.

When using a TabBar, construct TabManager with the 5-parameter constructor:

TabBar bar = new MyModTabBar();
TabManager manager = new TabManager(screen, buttonList, width, height, bar);

The 4-parameter constructor still works without a TabBar for full backward compatibility.

TabState

Package: decok.dfcdvadstf.catframe.ui.tab.TabState

An enum that defines texture coordinates and text colors for the four tab interaction states — NORMAL, HOVER, SELECTED, SELECTED_HOVER. Used by the tab button renderer to pick the right texture slice and color.

State Texture (u, v) Text Color
NORMAL (0, 0) White (0xFFFFFF)
HOVER (0, 24) Yellow (0xFFFF55)
SELECTED (0, 48) White (0xFFFFFF)
SELECTED_HOVER (0, 72) Yellow (0xFFFF55)

TabBar

Package: decok.dfcdvadstf.catframe.ui.tab.TabBar

TabBar is an abstract container bar for tabs. It provides a common background for the tab area and holds the tabs that belong to it. Each TabBar has a unique ID, and subclasses customise the look (solid colour fill + optional tiled texture, defaults to solid black).

This is the piece you extend if you want a distinct visual style for your screen's tab area.

Properties

Property Type Default Description
barId String (required) Unique identifier for this bar, set via constructor.
backgroundColor int (optional) default: 0xFF000000 Solid background colour in ARGB format.
backgroundTexture ResourceLocation (optional) default: null Optional tiled background texture (16×16 tile).
tabTexture ResourceLocation (optional) default: catframe:textures/gui/tabs.png Default tab button texture for all tabs in this bar.

Key Methods

Method What it does
drawBackground(x, y, width, height) Draws the bar background — solid colour fill first, then optional tiled texture on top.
registerEntry(TabEntry) Register a TabRegistry.TabEntry into this bar. Called automatically by TabManager during construction.
getAllEntries() Get all registered entries (ordered by insertion).
getEntry(int tabId) Get an entry by its tab ID.
getOrCreateTab(int tabId) Get an existing tab instance, or create one lazily from its entry's factory.
registerTab(Tab) Register a tab instance directly into this bar.
getAllTabs() Get all tab instances (ordered by creation).
getTab(int tabId) Get a tab by its ID.
containsTab(int tabId) Check if this bar contains a tab with the given ID.
getTabCount() Get the number of tab instances.
getBarId() Get the unique bar identifier.

Subclasses may override drawTiledBackground(x, y, width, height) to customise how the tiled texture is rendered.

Custom TabBar Example (After 0.0.3)

import decok.dfcdvadstf.catframe.ui.tab.TabBar;
import net.minecraft.util.ResourceLocation;

public class MyModTabBar extends TabBar {

    public MyModTabBar() {
        super("my_mod_bar");           // Unique bar ID
        setBackgroundColor(0xFF2D2D2D); // Optional dark grey solid fill
        setBackgroundTexture(           // Optional tiled texture
            new ResourceLocation("mymod", "textures/gui/bar_bg.png"));
        setTabTexture(                  // Optional: override default tab texture
            new ResourceLocation("mymod", "textures/gui/tab_buttons.png"));
    }
}

Notes: Although except bar ID, rest of these are optional and can leave it unimplemented, this will end up with a soild black fill. Which means: public MyModTabBar() {super("my_mod_bar");} equals

public MyModTabBar() {
  super("my_mod_bar");
  setBackgroundColor("0XFF000000");
  setTabTexture(
  new ResourceLocation("catframe", "textures/gui/tabs/buttons.png"));
}

Usage with TabManager

// 1. Register tabs during preInit (as before)
TabRegistry.registerTab(MyCustomTab::new, 103, "mymod.tab.custom", 5);

// 2. Create your TabBar
TabBar bar = new MyModTabBar();

// 3. Pass it to TabManager
TabManager manager = new TabManager(screen, buttonList, width, height, bar);

TabRegistry

Package: decok.dfcdvadstf.catframe.ui.tab.TabRegistry

The static registry for external mods to register custom tabs. Register your tabs during mod init, and when the screen opens, TabManager freezes the registry and creates all the tabs. Simple as that.

Key Methods

Method What it does
registerTab(Supplier<Tab>, int tabId, String nameKey, int priority) Register a tab with factory, ID, name key, and priority.
registerTab(Supplier<Tab>, int tabId, String nameKey) Same thing, but priority defaults to tabId.
getEntries() Get all entries sorted by priority (unmodifiable).
isFrozen() Check if the registry is frozen (GUI already initialized).
freeze() Freeze the registry. Called internally by TabManager.

Parameters

Parameter Type What it means
factory Supplier<Tab> A factory that creates your Tab instance. Called once when TabManager is constructed.
tabId int Unique tab ID. Built-in tabs use 100–102, so start from 103.
nameKey String Localization key for the tab name (like "mymod.tab.custom").
priority int Sort order — lower comes first. Defaults to tabId if not specified. Built-in tabs use 0, 1, 2.

Entries are sorted by priority ascending, then tabId ascending as a tiebreaker.

Registration Example

import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import decok.dfcdvadstf.catframe.ui.tab.AbstractScreenTab;
import decok.dfcdvadstf.catframe.ui.tab.Tab;
import decok.dfcdvadstf.catframe.ui.tab.TabManager;
import decok.dfcdvadstf.catframe.ui.tab.TabRegistry;
import net.minecraft.client.gui.GuiButton;

@Mod(modid = "mymod", name = "My Mod", version = "1.0")
public class MyMod {

    @Mod.EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        TabRegistry.registerTab(
            MyCustomTab::new,        // Supplier<Tab> factory
            103,                     // Unique tab ID (start from 103)
            "mymod.tab.custom",      // Localization key
            5                        // Sort priority (lower = earlier)
        );
    }
}

And don't forget the localization in your lang file:

mymod.tab.custom=My Tab

Things to Keep in Mind

  • Register early — like FMLPreInitializationEvent. Once TabManager is constructed the registry freezes, and registering after that throws an IllegalStateException.
  • Tab IDs must be unique — duplicate IDs throw an IllegalArgumentException.
  • AbstractScreenTab constructor must match — the tabId and nameKey you pass there need to be the same ones you gave TabRegistry.registerTab().
  • initGui() gets called by TabManager — on init and on resize. Always call super.initGui() and set setVisible(false) at the end.

Dependency Setup

To use CatFrame as a dependency, add this to your build.gradle:

dependencies {
    implementation 'com.github.song682:CatFrame:Tag'
}

Replace Tag with the version you want from the repository.

Clone this wiki locally