Creating a custom skin

Creating a Custom Skin

This section guides you through creating a custom Skin to define a unique context menu appearance.

Steps

  1. Extend Skin:

    • Create a new class extending Skin.

    • Optionally implement GroupableSkin for option group support.

  2. Register Renderers:

    • In the constructor, use addRenderer to register SkinRenderer instances for supported option types.

  3. Implement renderContextMenu:

    • Draw the menu’s background and layout.

    • Iterate over getOptions to render each option using its renderer.

  4. Handle Input:

    • Override input methods (mouseClicked, etc.) for menu-level interactions.

    • Delegate option-specific input to renderers.

  5. Set Screen Behavior:

    • Use setCreateNewScreen to determine if the menu opens in a new screen.

Example:

public class SimpleSkin extends Skin {
    public SimpleSkin() {
        addRenderer(BooleanOption.class, SimpleBooleanRenderer::new);
        setCreateNewScreen(false); // This will open this skin in the moveable screen itself. eg: classic skin
    }

    @Override
    public void renderContextMenu(DrawContext drawContext, ContextMenu<?> contextMenu, int mouseX, int mouseY) {
        this.contextMenu = contextMenu;
        //Some padding and dimension definition
        int yOffset = contextMenu.y + 2;
        int width = 10;

        // Draw background
        DrawHelper.drawRectangle(drawContext.getMatrices().peek().getPositionMatrix(),
            contextMenu.x, contextMenu.y, 100, contextMenu.getHeight(), 0x80000000);

        // Render options
        // You MUST use getOptions(contextMenu) so you avoid problems with setting groups
        for (Option<?> option : getOptions(contextMenu)) {
            if (!option.shouldRender()) continue;
            option.render(drawContext, contextMenu.x + 2, yOffset, mouseX, mouseY);
            width = Math.max(width, option.getWidth());
            yOffset += option.getHeight() + 1;
        }

        contextMenu.setWidth(width + 4);
        contextMenu.setHeight(yOffset - contextMenu.y);
    }

    public static class SimpleBooleanRenderer implements SkinRenderer<BooleanOption> {
        @Override
        public void render(DrawContext drawContext, BooleanOption option, int x, int y, int mouseX, int mouseY) {
            option.setPosition(x, y);
            option.setHeight(10);
            option.setWidth(mc.textRenderer.getWidth(option.getName().getString()) + 10);
            int color = option.get() ? 0x00FF00 : 0xFF0000;
            drawContext.drawText(mc.textRenderer, option.getName(), x, y, color, false);
        }
    }
}

Common Pitfalls

  • Not Setting Dimensions: Failing to update contextMenu.setWidth and contextMenu.setHeight can cause clipping.

    • Fix: Calculate and set dimensions based on rendered options.

  • Ignoring Group Support: If supportsGroups() is false, groups are flattened, which may break hierarchical menus.

    • Fix: Implement GroupableSkin if groups are needed.

You MUST use getOptions(contextMenu) so you avoid problems with setting groups

Last updated

Was this helpful?