diff --git a/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/api/client/screen/v1/ScreenKeyboardEvents.java b/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/api/client/screen/v1/ScreenKeyboardEvents.java index 1fb66ff46d..2f999e2ec7 100644 --- a/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/api/client/screen/v1/ScreenKeyboardEvents.java +++ b/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/api/client/screen/v1/ScreenKeyboardEvents.java @@ -19,6 +19,7 @@ import java.util.Objects; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.input.CharInput; import net.minecraft.client.input.KeyInput; import net.fabricmc.fabric.api.event.Event; @@ -50,7 +51,7 @@ public static Event allowKeyPress(Screen screen) { } /** - * An event that is called before a key press is processed for a screen. + * An event called before a key press is processed for a screen. * * @return the event */ @@ -61,7 +62,7 @@ public static Event beforeKeyPress(Screen screen) { } /** - * An event that is called after a key press is processed for a screen. + * An event called after a key press is processed for a screen. * * @return the event */ @@ -83,7 +84,7 @@ public static Event allowKeyRelease(Screen screen) { } /** - * An event that is called after the release of a key is processed for a screen. + * An event called before the release of a key is processed for a screen. * * @return the event */ @@ -94,7 +95,7 @@ public static Event beforeKeyRelease(Screen screen) { } /** - * An event that is called after the release a key is processed for a screen. + * An event called after the release a key is processed for a screen. * * @return the event */ @@ -104,6 +105,39 @@ public static Event afterKeyRelease(Screen screen) { return ScreenExtensions.getExtensions(screen).fabric_getAfterKeyReleaseEvent(); } + /** + * An event that checks if typing a character should be allowed. + * + * @return the event + */ + public static Event allowCharType(Screen screen) { + Objects.requireNonNull(screen, "Screen cannot be null"); + + return ScreenExtensions.getExtensions(screen).fabric_getAllowCharTypeEvent(); + } + + /** + * An event called before typing a character is processed for a screen. + * + * @return the event + */ + public static Event beforeCharType(Screen screen) { + Objects.requireNonNull(screen, "Screen cannot be null"); + + return ScreenExtensions.getExtensions(screen).fabric_getBeforeCharTypeEvent(); + } + + /** + * An event called after typing a character is processed for a screen. + * + * @return the event + */ + public static Event afterCharType(Screen screen) { + Objects.requireNonNull(screen, "Screen cannot be null"); + + return ScreenExtensions.getExtensions(screen).fabric_getAfterCharTypeEvent(); + } + private ScreenKeyboardEvents() { } @@ -180,4 +214,41 @@ public interface AfterKeyRelease { */ void afterKeyRelease(Screen screen, KeyInput context); } + + @FunctionalInterface + public interface AllowCharType { + /** + * Checks if typing a character should be allowed. + * + * @param context the context of typing the character, containing the codepoint and modifiers + * @return whether the character should be typed + * @see CharInput#asString() + * @see Modifier key flags + */ + boolean allowCharType(Screen screen, CharInput context); + } + + @FunctionalInterface + public interface BeforeCharType { + /** + * Called before a character is typed. + * + * @param context the context of typing the character, containing the codepoint and modifiers + * @see CharInput#asString() + * @see Modifier key flags + */ + void beforeCharType(Screen screen, CharInput context); + } + + @FunctionalInterface + public interface AfterCharType { + /** + * Called after a character is typed. + * + * @param context the context of typing the character, containing the codepoint and modifiers + * @see CharInput#asString() + * @see Modifier key flags + */ + void afterCharType(Screen screen, CharInput context); + } } diff --git a/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/impl/client/screen/ScreenEventFactory.java b/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/impl/client/screen/ScreenEventFactory.java index 6ff223db09..dd6107b8ad 100644 --- a/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/impl/client/screen/ScreenEventFactory.java +++ b/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/impl/client/screen/ScreenEventFactory.java @@ -132,6 +132,34 @@ public static Event createAfterKeyReleaseE }); } + public static Event createAllowCharTypeEvent() { + return EventFactory.createArrayBacked(ScreenKeyboardEvents.AllowCharType.class, callbacks -> (screen, context) -> { + for (ScreenKeyboardEvents.AllowCharType callback : callbacks) { + if (!callback.allowCharType(screen, context)) { + return false; + } + } + + return true; + }); + } + + public static Event createBeforeCharTypeEvent() { + return EventFactory.createArrayBacked(ScreenKeyboardEvents.BeforeCharType.class, callbacks -> (screen, context) -> { + for (ScreenKeyboardEvents.BeforeCharType callback : callbacks) { + callback.beforeCharType(screen, context); + } + }); + } + + public static Event createAfterCharTypeEvent() { + return EventFactory.createArrayBacked(ScreenKeyboardEvents.AfterCharType.class, callbacks -> (screen, context) -> { + for (ScreenKeyboardEvents.AfterCharType callback : callbacks) { + callback.afterCharType(screen, context); + } + }); + } + // Mouse Events public static Event createAllowMouseClickEvent() { diff --git a/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/impl/client/screen/ScreenExtensions.java b/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/impl/client/screen/ScreenExtensions.java index d7b6d443ef..e79a70ee32 100644 --- a/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/impl/client/screen/ScreenExtensions.java +++ b/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/impl/client/screen/ScreenExtensions.java @@ -59,6 +59,12 @@ static ScreenExtensions getExtensions(Screen screen) { Event fabric_getAfterKeyReleaseEvent(); + Event fabric_getAllowCharTypeEvent(); + + Event fabric_getBeforeCharTypeEvent(); + + Event fabric_getAfterCharTypeEvent(); + // Mouse Event fabric_getAllowMouseClickEvent(); diff --git a/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/mixin/screen/KeyboardMixin.java b/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/mixin/screen/KeyboardMixin.java index 6e5833824a..a3f8db4333 100644 --- a/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/mixin/screen/KeyboardMixin.java +++ b/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/mixin/screen/KeyboardMixin.java @@ -23,6 +23,7 @@ import net.minecraft.client.Keyboard; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.input.CharInput; import net.minecraft.client.input.KeyInput; import net.fabricmc.fabric.api.client.screen.v1.ScreenKeyboardEvents; @@ -36,7 +37,7 @@ private boolean invokeKeyPressedEvents(Screen screen, KeyInput ctx, Operation operation) { + // The screen passed to events is the same as the screen the handler method is called on, + // regardless of whether the screen changes within the handler or event invocations. + + if (screen != null) { + if (!ScreenKeyboardEvents.allowCharType(screen).invoker().allowCharType(screen, charInput)) { + // Set this action as handled + return true; + } + + ScreenKeyboardEvents.beforeCharType(screen).invoker().beforeCharType(screen, charInput); + } + + boolean result = operation.call(screen, charInput); + + if (screen != null) { + ScreenKeyboardEvents.afterCharType(screen).invoker().afterCharType(screen, charInput); + } + + return result; + } } diff --git a/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/mixin/screen/ScreenMixin.java b/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/mixin/screen/ScreenMixin.java index 9e6c23ad6c..744adf8e19 100644 --- a/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/mixin/screen/ScreenMixin.java +++ b/fabric-screen-api-v1/src/client/java/net/fabricmc/fabric/mixin/screen/ScreenMixin.java @@ -82,6 +82,12 @@ abstract class ScreenMixin implements ScreenExtensions { private Event beforeKeyReleaseEvent; @Unique private Event afterKeyReleaseEvent; + @Unique + private Event allowCharTypeEvent; + @Unique + private Event beforeCharTypeEvent; + @Unique + private Event afterCharTypeEvent; // Mouse @Unique @@ -152,6 +158,9 @@ private void beforeInit(MinecraftClient client, int width, int height) { this.allowKeyReleaseEvent = ScreenEventFactory.createAllowKeyReleaseEvent(); this.beforeKeyReleaseEvent = ScreenEventFactory.createBeforeKeyReleaseEvent(); this.afterKeyReleaseEvent = ScreenEventFactory.createAfterKeyReleaseEvent(); + this.allowCharTypeEvent = ScreenEventFactory.createAllowCharTypeEvent(); + this.beforeCharTypeEvent = ScreenEventFactory.createBeforeCharTypeEvent(); + this.afterCharTypeEvent = ScreenEventFactory.createAfterCharTypeEvent(); // Mouse this.allowMouseClickEvent = ScreenEventFactory.createAllowMouseClickEvent(); @@ -256,6 +265,21 @@ public Event fabric_getAfterKeyReleaseEven return ensureEventsAreInitialized(this.afterKeyReleaseEvent); } + @Override + public Event fabric_getAllowCharTypeEvent() { + return ensureEventsAreInitialized(this.allowCharTypeEvent); + } + + @Override + public Event fabric_getBeforeCharTypeEvent() { + return ensureEventsAreInitialized(this.beforeCharTypeEvent); + } + + @Override + public Event fabric_getAfterCharTypeEvent() { + return ensureEventsAreInitialized(this.afterCharTypeEvent); + } + // Mouse @Override diff --git a/fabric-screen-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/screen/ScreenTests.java b/fabric-screen-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/screen/ScreenTests.java index b87fea9d15..a4b2cf1ed3 100644 --- a/fabric-screen-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/screen/ScreenTests.java +++ b/fabric-screen-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/screen/ScreenTests.java @@ -80,12 +80,17 @@ private void afterInitScreen(MinecraftClient client, Screen screen, int windowWi .orElseThrow(() -> new AssertionError("Failed to find the \"Stop Sound\" button in the screen's elements")); ScreenKeyboardEvents.allowKeyPress(screen).register((_screen, context) -> { - LOGGER.info("After Pressed, Context: {}", context); + LOGGER.info("Allow Key Press, Context: {}", context); return true; // Let actions continue }); ScreenKeyboardEvents.afterKeyPress(screen).register((_screen, context) -> { - LOGGER.warn("Pressed, Context: {}", context); + LOGGER.warn("After Key Press, Context: {}", context); + }); + + ScreenKeyboardEvents.allowCharType(screen).register((_screen, context) -> { + LOGGER.warn("Allow Char Type, Context: {}, Character: {}", context, context.asString()); + return true; }); } else if (screen instanceof CreativeInventoryScreen) { Screens.getButtons(screen).add(new TestButtonWidget());