Skip to content

Initialization

fishstiz edited this page Mar 12, 2025 · 6 revisions

Important

The Minecraft Cursor Wiki has moved to:
https://fishstiz.github.io/minecraft-cursor-wiki/.

This page will no longer be updated.



Register cursor types and elements using the MinecraftCursorInitializer interface.

Note: Minecraft Cursor does not have to be a required dependency for MinecraftCursorInitializer. The class will only be loaded when it needs to.

Setup

Create your initializer class and implement MinecraftCursorInitializer. This is the entrypoint for Minecraft Cursor.

public class MyMinecraftCursorInitializer implements MinecraftCursorInitializer {
    @Override
    public void init(CursorTypeRegistrar cursorRegistrar, ElementRegistrar elementRegistrar) {
        // The CursorTypeRegistrar and ElementRegistrar instances are injected here for registration.
        // This method is invoked on client initialization of Minecraft Cursor.
    }
}

Register the Entrypoint

Fabric: Add the initializer in fabric.mod.json with the minecraft-cursor key.

"entrypoints": {
  "minecraft-cursor": ["com.example.modid.MyMinecraftCursorInitializer"]
}

Neoforge & Forge: Create a services directory under META-INF, then create a file named with the fully qualified class name of the initializer:

resources/
└── META-INF/
    └── services/
        └── io.github.fishstiz.minecraftcursor.api.MinecraftCursorInitializer

Place the fully qualified class name of your MinecraftCursorInitializer implementation inside the file.

io.github.fishstiz.minecraftcursor.api.MinecraftCursorInitializer:

com.example.modid.MyMinecraftCursorInitializer

CursorType Registration

After creating your cursor types, you must register them here using CursorTypeRegistrar#register(CursorType...) so the assets can be retrieved on resource reloads.

Note: You should only register your custom cursor type within MinecraftCursorInitializer#init(CursorTypeRegistrar, ElementRegistrar) to ensure that your custom cursor type's assets will be loaded on the initial resource load of Minecraft.

Example usage of CursorTypeRegistrar#register

public class MyMinecraftCursorInitializer implements MinecraftCursorInitializer {
    static final CursorType STATIC_CURSOR_TYPE = CursorType.of("static-cursor-type");

    @Override
    public void init(CursorTypeRegistrar cursorRegistrar, ElementRegistrar elementRegistrar) {
        // Register STATIC_CURSOR_TYPE
        cursorRegistrar.register(STATIC_CURSOR_TYPE)

        // Creating a CursorType inline
        CursorType customCursor = CursorType.of("custom-cursor");
        // Register customCursor
        cursorRegistrar.register(customCursor);

        // Create and register a cursor type inline at the same time
        CursorType myCursor = cursorRegistrar.register("my-cursor");

        // Registering multiple cursor types
        cursorRegistrar.register(STATIC_CURSOR_TYPE, customCursor, myCursor);

        // Registering cursor types from a CursorType enum
        cursorRegistrar.register(CustomCursorEnum.values());
    }

    // Example implementation of CursorType on an enum
    enum CustomCursorEnum implements CursorType {
        CROSSHAIR,
        NOT_ALLOWED;

        @Override
        public String getKey() {
            return this.name().toLowerCase();
        }
    }
}

Element Registration

Using ElementRegistrar#register(Element, CursorTypeFunction), register elements with a function that determines the cursor type to be applied when the the mouse is on it, meaning Element#isMouseOver(mouseX, mouseY) returns true.

Note: The element must be an instance of Element. It must either be the current screen itself or be accessible from the current screen or from its parent element using the ParentElement#children() method.

Any container or nested container must be an instance of ParentElement and accessible via the ParentElement#children() method. This accessibility must be maintained through the entire element hierarchy, starting from the current screen down to the deepest nested parent element.

For Mojang Mappings: Element = GuiEventListener, and ParentElement = ContainerEventListener

Register with a Class Reference

public class MyMinecraftCursorInitializer implements MinecraftCursorInitializer {
    @Override
    public void init(CursorTypeRegistrar cursorRegistrar, ElementRegistrar elementRegistrar) {
        // Register MyButton with the built-in pointer cursor type
        elementRegistrar.register(MyButton.class, (myButton, mouseX, mouseY) -> CursorType.POINTER);

        // You can use the built-in static methods of ElementRegistrar when no additional logic is needed
        elementRegistrar.register(MyOtherButton.class, ElementRegistrar::elementToPointer);

        // Register MyCustomElement with a custom cursor type
        CursorType customCursor = cursorRegistrar.register("custom-cursor");
        elementRegistrar.register(MyCustomElement.class, (myCustomElement, mouseX, mouseY) -> customCursor);
    }
}

Register with a Fully Qualified Class Name (FQCN)

If you cannot access a class directly, you can register it using its fully qualified class name (FQCN)

Note: Use the intermediary mappings when registering a native Minecraft Element in Fabric.

public class MyMinecraftCursorInitializer implements MinecraftCursorInitializer {
    @Override
    public void init(CursorTypeRegistrar cursorRegistrar, ElementRegistrar elementRegistrar) {
        // Register PressableWidget using its fully qualified class name in Fabric with the built-in pointer cursor type 
        elementRegistrar.register("net.minecraft.class_4264", (pressableWidget, mouseX, mouseY) -> CursorType.POINTER);
    }
}

Register with a CursorHandler

CursorHandler is an interface used when the cursor type computing logic gets too large or complex, or you simply want to it to be in a separate class file.

public class MyMinecraftCursorInitializer implements MinecraftCursorInitializer {
    @Override
    public void init(CursorTypeRegistrar cursorRegistrar, ElementRegistrar elementRegistrar) {
        // inline CursorHandler
        elementRegistrar.register(new CursorHandler<MyButton>() {
            @Override
            public CursorType getCursorType(MyButton myButton, double mouseX, double mouseY) {
                return CursorType.POINTER;
            }
        });
    }
}

In a separate class

Implement CursorHandler to a class for more complex computations, better readability, and clearer separation of concerns.

// The type passed will be the element to be registered. In this case, it is MyElement. 
public class MyElementCursorHandler implements CursorHandler<MyElement> { 
    @Override
    public CursorType getCursorType(MyElement myElement, double mouseX, double mouseY) {
        // This is the cursor type function to be associated with MyElement, invoked when MyElement.isMouseOver returns true
        return CursorType.POINTER;
    }
}
// Register the CursorHandler
public class MyMinecraftCursorInitializer implements MinecraftCursorInitializer {
    @Override
    public void init(CursorTypeRegistrar cursorRegistrar, ElementRegistrar elementRegistrar) {
        elementRegistrar.register(new MyElementCursorHandler());
    }
}

Using the Fully Qualified Class Name (FQCN) of the Element

When the target element is inaccessible, you can simply pass the Element generic type and override the CursorHandler#getTargetElement() method to return the FQCN of the target element.

Note: Use the intermediary mappings when registering a native Minecraft Element in Fabric.

// Pass the Element superclass as placeholder
public class MerchantScreenButtonCursorHandler implements CursorHandler<Element> {
    @Override
    public @NotNull TargetElement<Element> getTargetElement() { // Override this method to change the target element
        // return a TargetElement with the fully qualified class name
        // Use intermediary mappings for Minecraft elements when using Fabric
        return TargetElement.fromClassName("net.minecraft.class_492$class_493"); 
    }

    @Override
    public CursorType getCursorType(Element element, double mouseX, double mouseY) {
        return CursorTypeUtil.canShift() ? CursorType.SHIFT : CursorType.POINTER;
    }
}
// Register the CursorHandler as usual
public class MyMinecraftCursorInitializer implements MinecraftCursorInitializer {
    @Override
    public void init(CursorTypeRegistrar cursorRegistrar, ElementRegistrar elementRegistrar) {
        cursorTypeRegistrar.register(new MerchantScreenButtonCursorHandler());
    }
}

Registering a HandledScreen (AbstractContainerScreen in Mojang Mappings) subclass

HandledScreen elements must be handled differently so that the HandledScreen built-in cursor handler does not get overriden.

The HandledScreen cursor handler is responsible for:

  • Changing the cursor type to shift when holding shift above items in the inventory.
  • Changing the cursor type to pointer when hovering over items in the inventory.
  • Changing the cursor type to grabbing when an item is held in the inventory.

You can either replicate this behavior in your HandledScreen subclass, or extend the AbstractHandledScreenCursorHandler, or better yet, implement CursorProvider directly in your element so you do not need to do register it or provide additional logic for the HandleScreen superclass.

Example extending AbstractHandledScreenCursorHandler

// AbstractHandledScreenCursorHandler accepts two parameters:
// The first parameter is the ScreenHandler of the HandledScreen subclass you wish to be registered.
// The second parameter is the HandledScreen subclass itself.
public class MyHandledScreenCursorHandler extends AbstractHandledScreenCursorHandler<MyScreenHandler, MyHandledScreen> {
    @Override
    public CursorType getCursorType(MyHandledScreen handledScreen, double mouseX, double mouseY) {
        // There are two approaches:

        // 1. Get the cursor type of the HandledScreen first.
        CursorType cursorType = super.getCursorType(handledScreen, mouseX, mouseY);
        // If the cursor type of the handled screen is not default then return the cursor type 
        if (cursorType != CursorType.DEFAULT) return cursorType;
        // You can also return later if you need the state of the cursor type from the HandledScreen.

        // 2. Return the cursor type of the HandledScreen last after computing the cursor type
        // of your HandledScreen and none of your conditions were met. 
        return super.getCursorType(handledScreen, mouseX, mouseY);
    }
}

Practical Examples

For more examples, you can take a look at the source code of Minecraft Cursor: https://github.com/fishstiz/minecraft-cursor/blob/release/1.21.4/common/src/main/java/io/github/fishstiz/minecraftcursor/impl/MinecraftCursorInitializerImpl.java#L18


Previous: Create Cursor Types
Create your own custom cursor types.

Next: CursorProvider Interface
A more declarative alternative to Element Registration.

Clone this wiki locally