Screen API v1 ()

* Implement screen api

* Actually update to 20w27a

* Split render and tick events to before and after

* Rename and update

* A bit of javadoc and profiler name fixes

* Add functional interface annotations, move render and ticking events to actual FabricScreen instance.

* Add after screen resize event

* Implement key and mouse click/press and release events.

* Move keyboard and mouse events to their own pojos

* Init and resize are the same thing. Maybe Screen#init needs a rename

* Add mouse scroll events

* checkstyle

* Refresh event instances after init

* Before init is nessecary to listen to addition/removal of child elements

* Polish up the javadoc and do a rename to the public api interface.

* Mappings updates on testmod

* javadoc formatting again

* Rework screen api design to be more ergonomic.

* Add remove event, some javadoc

* Add allow phase

* Module dependencies

* Fix null ticking when no screen is open

* Refer to GLFW constants in mouse click/release events

* Keyboard event GLFW constant javadoc

* Remove redundant qualifier

* Some docs, degetterifying

* Because global go brr add screen params back around

* Add module lifecycle to FMJ

(cherry picked from commit 8e23c1d877)
This commit is contained in:
i509VCB 2021-01-25 12:17:17 -06:00 committed by modmuss50
parent 36b77c3e9f
commit 9af2c302f9
23 changed files with 2117 additions and 0 deletions

View file

@ -0,0 +1,6 @@
archivesBaseName = "fabric-screen-api-v1"
version = getSubprojectVersion(project, "1.0.0")
moduleDependencies(project, [
'fabric-api-base'
])

View file

@ -0,0 +1,209 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.client.screen.v1;
import java.util.Objects;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.TickableElement;
import net.minecraft.client.util.math.MatrixStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.fabricmc.fabric.impl.client.screen.ScreenExtensions;
/**
* Holds events related to {@link Screen}s.
*
* <p>Some events require a screen instance in order to obtain an event instance.
* The events that require a screen instance can be identified by the use of a method passing a screen instance.
* All events in {@link ScreenKeyboardEvents} and {@link ScreenMouseEvents} require a screen instance.
* This registration model is used since a screen being (re)initialized will reset the screen to it's default state, therefore reverting all changes a mod developer may have applied to a screen.
* Furthermore this design was chosen to reduce the amount of wasted iterations of events as a mod developer would only need to register screen events for rendering, ticking, keyboards and mice if needed on a per instance basis.
*
* <p>The primary entrypoint into a screen is when it is being opened, this is signified by an event {@link ScreenEvents#BEFORE_INIT before} and {@link ScreenEvents#AFTER_INIT after} initialization of the screen.
*
* @see Screens
* @see ScreenKeyboardEvents
* @see ScreenMouseEvents
*/
@Environment(EnvType.CLIENT)
public final class ScreenEvents {
/**
* An event that is called before {@link Screen#init(MinecraftClient, int, int) a screen is initialized} to it's default state.
* It should be noted some of the methods in {@link Screens} such as a screen's {@link Screens#getTextRenderer(Screen) text renderer} may not be initialized yet, and as such their use is discouraged.
*
* <!--<p>Typically this event is used to register screen events such as listening to when child elements are added to the screen. ------ Uncomment when child add/remove event is added for elements-->
* You can still use {@link ScreenEvents#AFTER_INIT} to register events such as keyboard and mouse events.
*
* <p>The {@link ScreenExtensions} provided by the {@code info} parameter may be used to register tick, render events, keyboard, mouse, additional and removal of child elements (including buttons).
* For example, to register an event on inventory like screens after render, the following code could be used:
* <pre>{@code
* &#64;Override
* public void onInitializeClient() {
* ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
* if (screen instanceof AbstractInventoryScreen) {
* ScreenEvents.getAfterRenderEvent(screen).register((matrices, mouseX, mouseY, tickDelta) -> {
* ...
* });
* }
* });
* }
* }</pre>
*
* <p>This event indicates a screen has been resized, and therefore is being re-initialized.
* This event can also indicate that the previous screen has been changed.
* @see ScreenEvents#AFTER_INIT
*/
public static final Event<BeforeInit> BEFORE_INIT = EventFactory.createArrayBacked(BeforeInit.class, callbacks -> (client, screen, scaledWidth, scaledHeight) -> {
for (BeforeInit callback : callbacks) {
callback.beforeInit(client, screen, scaledWidth, scaledHeight);
}
});
/**
* An event that is called after {@link Screen#init(MinecraftClient, int, int) a screen is initialized} to it's default state.
*
* <p>Typically this event is used to modify a screen after the screen has been initialized.
* Modifications such as changing sizes of buttons, removing buttons and adding/removing child elements to the screen can be done safely using this event.
*
* <p>For example, to add a button to the title screen, the following code could be used:
* <pre>{@code
* ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
* if (screen instanceof TitleScreen) {
* Screens.getButtons(screen).add(new ButtonWidget(...));
* }
* });
* }</pre>
*
* <p>Note that by adding an element to a screen, the element is not automatically {@link net.minecraft.client.gui.screen.TickableElement ticked} or {@link net.minecraft.client.gui.Drawable drawn}.
* Unless the element is button, you need to call the specific {@link TickableElement#tick() tick} and {@link net.minecraft.client.gui.Drawable#render(MatrixStack, int, int, float) render} methods in the corresponding screen events.
*
* <p>This event can also indicate that the previous screen has been closed.
* @see ScreenEvents#BEFORE_INIT
*/
public static final Event<AfterInit> AFTER_INIT = EventFactory.createArrayBacked(AfterInit.class, callbacks -> (client, screen, scaledWidth, scaledHeight) -> {
for (AfterInit callback : callbacks) {
callback.afterInit(client, screen, scaledWidth, scaledHeight);
}
});
/**
* An event that is called after {@link Screen#removed()} is called.
* This event signifies that the screen is now closed.
*
* <p>This event is typically used to undo any screen specific state changes such as setting the keyboard to receive {@link net.minecraft.client.Keyboard#setRepeatEvents(boolean) repeat events} or terminate threads spawned by a screen.
* This event may precede initialization events {@link ScreenEvents#BEFORE_INIT} but there is no guarantee that event will be called immediately afterwards.
*/
public static Event<Remove> remove(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getRemoveEvent();
}
/**
* An event that is called before a screen is rendered.
*
* @return the event
*/
public static Event<BeforeRender> beforeRender(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getBeforeRenderEvent();
}
/**
* An event that is called after a screen is rendered.
*
* @return the event
*/
public static Event<AfterRender> afterRender(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAfterRenderEvent();
}
/**
* An event that is called before a screen is ticked.
*
* @return the event
*/
public static Event<BeforeTick> beforeTick(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getBeforeTickEvent();
}
/**
* An event that is called after a screen is ticked.
*
* @return the event
*/
public static Event<AfterTick> afterTick(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAfterTickEvent();
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface BeforeInit {
void beforeInit(MinecraftClient client, Screen screen, int scaledWidth, int scaledHeight);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AfterInit {
void afterInit(MinecraftClient client, Screen screen, int scaledWidth, int scaledHeight);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface Remove {
void onRemove(Screen screen);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface BeforeRender {
void beforeRender(Screen screen, MatrixStack matrices, int mouseX, int mouseY, float tickDelta);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AfterRender {
void afterRender(Screen screen, MatrixStack matrices, int mouseX, int mouseY, float tickDelta);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface BeforeTick {
void beforeTick(Screen screen);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AfterTick {
void afterTick(Screen screen);
}
private ScreenEvents() {
}
}

View file

@ -0,0 +1,203 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.client.screen.v1;
import java.util.Objects;
import net.minecraft.client.gui.screen.Screen;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.impl.client.screen.ScreenExtensions;
/**
* Events related to use of the keyboard in a {@link Screen}.
*
* <p>All of these events work on top of a specific screen instance.
* Subscriptions will only last as long as the screen itself, they'll disappear once the screen gets refreshed, closed or replaced.
* Use {@link ScreenEvents#BEFORE_INIT} to register the desired events every time it is necessary.
*
* <p>Events are fired in the following order:
* <pre>{@code AllowX -> BeforeX -> AfterX}</pre>
* If the result of the Allow event is false, then Before and After are not called.
*
* @see ScreenEvents
*/
@Environment(EnvType.CLIENT)
public final class ScreenKeyboardEvents {
/**
* An event that checks if a key press should be allowed.
*
* @return the event
*/
public static Event<AllowKeyPress> allowKeyPress(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAllowKeyPressEvent();
}
/**
* An event that is called before a key press is processed for a screen.
*
* @return the event
*/
public static Event<BeforeKeyPress> beforeKeyPress(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getBeforeKeyPressEvent();
}
/**
* An event that is called after a key press is processed for a screen.
*
* @return the event
*/
public static Event<AfterKeyPress> afterKeyPress(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAfterKeyPressEvent();
}
/**
* An event that checks if a pressed key should be allowed to release.
*
* @return the event
*/
public static Event<AllowKeyRelease> allowKeyRelease(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAllowKeyReleaseEvent();
}
/**
* An event that is called after the release of a key is processed for a screen.
*
* @return the event
*/
public static Event<BeforeKeyRelease> beforeKeyRelease(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getBeforeKeyReleaseEvent();
}
/**
* An event that is called after the release a key is processed for a screen.
*
* @return the event
*/
public static Event<AfterKeyRelease> afterKeyRelease(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAfterKeyReleaseEvent();
}
private ScreenKeyboardEvents() {
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AllowKeyPress {
/**
* Checks if a key should be allowed to be pressed.
*
* @param key the named key code which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}
* @param scancode the unique/platform-specific scan code of the keyboard input
* @param modifiers a GLFW bitfield describing the modifier keys that are held down
* @return whether the key press should be processed
* @see org.lwjgl.glfw.GLFW#GLFW_KEY_Q
* @see <a href="https://www.glfw.org/docs/3.3/group__mods.html">Modifier key flags</a>
*/
boolean allowKeyPress(Screen screen, int key, int scancode, int modifiers);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface BeforeKeyPress {
/**
* Called before a key press is handled.
*
* @param key the named key code which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}
* @param scancode the unique/platform-specific scan code of the keyboard input
* @param modifiers a GLFW bitfield describing the modifier keys that are held down
* @see org.lwjgl.glfw.GLFW#GLFW_KEY_Q
* @see <a href="https://www.glfw.org/docs/3.3/group__mods.html">Modifier key flags</a>
*/
void beforeKeyPress(Screen screen, int key, int scancode, int modifiers);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AfterKeyPress {
/**
* Called after a key press is handled.
*
* @param key the named key code which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}
* @param scancode the unique/platform-specific scan code of the keyboard input
* @param modifiers a GLFW bitfield describing the modifier keys that are held down
* @see org.lwjgl.glfw.GLFW#GLFW_KEY_Q
* @see <a href="https://www.glfw.org/docs/3.3/group__mods.html">Modifier key flags</a>
*/
void afterKeyPress(Screen screen, int key, int scancode, int modifiers);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AllowKeyRelease {
/**
* Checks if a pressed key should be allowed to be released.
*
* @param key the named key code which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}
* @param scancode the unique/platform-specific scan code of the keyboard input
* @param modifiers a GLFW bitfield describing the modifier keys that are held down
* @return whether the key press should be released
* @see org.lwjgl.glfw.GLFW#GLFW_KEY_Q
* @see <a href="https://www.glfw.org/docs/3.3/group__mods.html">Modifier key flags</a>
*/
boolean allowKeyRelease(Screen screen, int key, int scancode, int modifiers);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface BeforeKeyRelease {
/**
* Called before a pressed key has been released.
*
* @param key the named key code which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}
* @param scancode the unique/platform-specific scan code of the keyboard input
* @param modifiers a GLFW bitfield describing the modifier keys that are held down
* @see org.lwjgl.glfw.GLFW#GLFW_KEY_Q
* @see <a href="https://www.glfw.org/docs/3.3/group__mods.html">Modifier key flags</a>
*/
void beforeKeyRelease(Screen screen, int key, int scancode, int modifiers);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AfterKeyRelease {
/**
* Called after a pressed key has been released.
*
* @param key the named key code which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}
* @param scancode the unique/platform-specific scan code of the keyboard input
* @param modifiers a GLFW bitfield describing the modifier keys that are held down
* @see org.lwjgl.glfw.GLFW#GLFW_KEY_Q
* @see <a href="https://www.glfw.org/docs/3.3/group__mods.html">Modifier key flags</a>
*/
void afterKeyRelease(Screen screen, int key, int scancode, int modifiers);
}
}

View file

@ -0,0 +1,271 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.client.screen.v1;
import java.util.Objects;
import net.minecraft.client.gui.screen.Screen;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.impl.client.screen.ScreenExtensions;
/**
* Events related to use of the mouse in a {@link Screen}.
*
* <p>All of these events work on top of a specific screen instance.
* Subscriptions will only last as long as the screen itself, they'll disappear once the screen gets refreshed, closed or replaced.
* Use {@link ScreenEvents#BEFORE_INIT} to register the desired events every time it is necessary.
*
* <p>Events are fired in the following order:
* <pre>{@code AllowX -> BeforeX -> AfterX}</pre>
* If the result of the Allow event is false, then Before and After are not called.
*
* @see ScreenEvents
*/
@Environment(EnvType.CLIENT)
public final class ScreenMouseEvents {
/**
* An event that checks if the mouse click should be allowed.
*
* @return the event
*/
public static Event<AllowMouseClick> allowMouseClick(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAllowMouseClickEvent();
}
/**
* An event that is called before a mouse click is processed for a screen.
*
* @return the event
*/
public static Event<BeforeMouseClick> beforeMouseClick(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getBeforeMouseClickEvent();
}
/**
* An event that is called after a mouse click is processed for a screen.
*
* @return the event
*/
public static Event<AfterMouseClick> afterMouseClick(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAfterMouseClickEvent();
}
/**
* An event that checks if the mouse click should be allowed to release in a screen.
*
* @return the event
*/
public static Event<AllowMouseRelease> allowMouseRelease(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAllowMouseReleaseEvent();
}
/**
* An event that is called before the release of a mouse click is processed for a screen.
*
* @return the event
*/
public static Event<BeforeMouseRelease> beforeMouseRelease(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getBeforeMouseReleaseEvent();
}
/**
* An event that is called after the release of a mouse click is processed for a screen.
*
* @return the event
*/
public static Event<AfterMouseRelease> afterMouseRelease(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAfterMouseReleaseEvent();
}
/**
* An event that is checks if the mouse should be allowed to scroll in a screen.
*
* <p>This event tracks amount of vertical and horizontal scroll.
*
* @return the event
*/
public static Event<AllowMouseScroll> allowMouseScroll(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAllowMouseScrollEvent();
}
/**
* An event that is called after mouse scrolling is processed for a screen.
*
* <p>This event tracks amount of vertical and horizontal scroll.
*
* @return the event
*/
public static Event<BeforeMouseScroll> beforeMouseScroll(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getBeforeMouseScrollEvent();
}
/**
* An event that is called after mouse scrolling is processed for a screen.
*
* <p>This event tracks amount a mouse was scrolled both vertically and horizontally.
*
* @return the event
*/
public static Event<AfterMouseScroll> afterMouseScroll(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getAfterMouseScrollEvent();
}
private ScreenMouseEvents() {
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AllowMouseClick {
/**
* @param mouseX the x position of the mouse
* @param mouseY the y position of the mouse
* @param button the button number, which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}.
* @see org.lwjgl.glfw.GLFW#GLFW_MOUSE_BUTTON_1
*/
boolean allowMouseClick(Screen screen, double mouseX, double mouseY, int button);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface BeforeMouseClick {
/**
* @param mouseX the x position of the mouse
* @param mouseY the y position of the mouse
* @param button the button number, which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}.
* @see org.lwjgl.glfw.GLFW#GLFW_MOUSE_BUTTON_1
*/
void beforeMouseClick(Screen screen, double mouseX, double mouseY, int button);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AfterMouseClick {
/**
* @param mouseX the x position of the mouse
* @param mouseY the y position of the mouse
* @param button the button number, which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}.
* @see org.lwjgl.glfw.GLFW#GLFW_MOUSE_BUTTON_1
*/
void afterMouseClick(Screen screen, double mouseX, double mouseY, int button);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AllowMouseRelease {
/**
* Checks if the mouse click should be allowed to release in a screen.
*
* @param mouseX the x position of the mouse
* @param mouseY the y position of the mouse
* @param button the button number, which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}.
* @see org.lwjgl.glfw.GLFW#GLFW_MOUSE_BUTTON_1
*/
boolean allowMouseRelease(Screen screen, double mouseX, double mouseY, int button);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface BeforeMouseRelease {
/**
* Called before a mouse click has released in a screen.
*
* @param mouseX the x position of the mouse
* @param mouseY the y position of the mouse
* @param button the button number, which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}.
* @see org.lwjgl.glfw.GLFW#GLFW_MOUSE_BUTTON_1
*/
void beforeMouseRelease(Screen screen, double mouseX, double mouseY, int button);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AfterMouseRelease {
/**
* Called after a mouse click has released in a screen.
*
* @param mouseX the x position of the mouse
* @param mouseY the y position of the mouse
* @param button the button number, which can be identified by the constants in {@link org.lwjgl.glfw.GLFW GLFW}.
* @see org.lwjgl.glfw.GLFW#GLFW_MOUSE_BUTTON_1
*/
void afterMouseRelease(Screen screen, double mouseX, double mouseY, int button);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AllowMouseScroll {
/**
* Checks if the mouse should be allowed to scroll in a screen.
*
* @param mouseX the x position of the mouse
* @param mouseY the y position of the mouse
* @param horizontalAmount the horizontal scroll amount
* @param verticalAmount the vertical scroll amount
* @return whether the mouse should be allowed to scroll
*/
boolean allowMouseScroll(Screen screen, double mouseX, double mouseY, double horizontalAmount, double verticalAmount);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface BeforeMouseScroll {
/**
* Called before a mouse has scrolled on screen.
*
* @param mouseX the x position of the mouse
* @param mouseY the y position of the mouse
* @param horizontalAmount the horizontal scroll amount
* @param verticalAmount the vertical scroll amount
*/
void beforeMouseScroll(Screen screen, double mouseX, double mouseY, double horizontalAmount, double verticalAmount);
}
@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface AfterMouseScroll {
/**
* Called after a mouse has scrolled on screen.
*
* @param mouseX the x position of the mouse
* @param mouseY the y position of the mouse
* @param horizontalAmount the horizontal scroll amount
* @param verticalAmount the vertical scroll amount
*/
void afterMouseScroll(Screen screen, double mouseX, double mouseY, double horizontalAmount, double verticalAmount);
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.client.screen.v1;
import java.util.List;
import java.util.Objects;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.render.item.ItemRenderer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.impl.client.screen.ScreenExtensions;
import net.fabricmc.fabric.mixin.screen.ScreenAccessor;
/**
* Utility methods related to screens.
*
* @see ScreenEvents
*/
@Environment(EnvType.CLIENT)
public final class Screens {
/**
* Gets all of a screen's button widgets.
* The provided list allows for addition and removal of buttons from the screen.
* This method should be preferred over adding buttons directly to a screen's {@link Screen#children() child elements}.
*
* @return a list of all of a screen's buttons
*/
public static List<AbstractButtonWidget> getButtons(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ScreenExtensions.getExtensions(screen).fabric_getButtons();
}
/**
* Gets a screen's item renderer.
*
* @return the screen's item renderer
*/
public static ItemRenderer getItemRenderer(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ((ScreenAccessor) screen).getItemRenderer();
}
/**
* Gets a screen's text renderer.
*
* @return the screen's text renderer.
*/
public static TextRenderer getTextRenderer(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ((ScreenAccessor) screen).getTextRenderer();
}
public static MinecraftClient getClient(Screen screen) {
Objects.requireNonNull(screen, "Screen cannot be null");
return ((ScreenAccessor) screen).getClient();
}
private Screens() {
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Fabric Screen API v1.
*
* <p>The screen api provides events and utilities in this package are related to the lifecycle of a {@link net.minecraft.client.gui.screen.Screen}.
*
* <p>For general screen events see {@link net.fabricmc.fabric.api.client.screen.v1.ScreenEvents}.
*
* <p>For screen events related to the use of a mouse, see {@link net.fabricmc.fabric.api.client.screen.v1.ScreenMouseEvents}.
*
* <p>For screen events related to the use of a keyboard, see {@link net.fabricmc.fabric.api.client.screen.v1.ScreenKeyboardEvents}.
*
* @see net.fabricmc.fabric.api.client.screen.v1.Screens
*/
package net.fabricmc.fabric.api.client.screen.v1;

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.client.screen;
import java.util.AbstractList;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
// TODO: When events for listening to addition of child elements are added, fire events from this list.
@ApiStatus.Internal
@Environment(EnvType.CLIENT)
public final class ButtonList<T extends AbstractButtonWidget> extends AbstractList<T> {
private final Screen screen;
private final List<T> buttons;
private final List<Element> children;
public ButtonList(Screen screen, List<T> buttons, List<Element> children) {
this.screen = screen;
this.buttons = buttons;
this.children = children;
}
@Override
public T get(int index) {
return this.buttons.get(index);
}
@Override
public T set(int index, T element) {
this.remove(element); // verify / ensure no duplicates
final T existingButton = this.buttons.get(index);
int elementIndex = this.children.indexOf(existingButton);
if (elementIndex > -1) {
this.children.set(elementIndex, element);
}
return this.buttons.set(index, element);
}
@Override
public void add(int index, T element) {
this.rangeCheckForAdd(index); // verify index bounds
this.remove(element); // ensure no duplicates
this.buttons.add(index, element);
this.children.add(Math.min(this.children.size(), index), element);
}
@Override
public T remove(int index) {
this.rangeCheck(index); // verify index bounds
final T removedButton = this.buttons.remove(index);
this.children.remove(removedButton);
return removedButton;
}
@Override
public int size() {
return this.buttons.size();
}
private void rangeCheck(int index) {
if (index >= this.size()) {
throw createOutOfBoundsException(index);
}
}
private void rangeCheckForAdd(int index) {
if (index > this.size() || index < 0) {
throw createOutOfBoundsException(index);
}
}
private IndexOutOfBoundsException createOutOfBoundsException(int index) {
return new IndexOutOfBoundsException("Index: " + index + ", Size: "+ this.size());
}
}

View file

@ -0,0 +1,221 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.client.screen;
import org.jetbrains.annotations.ApiStatus;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
import net.fabricmc.fabric.api.client.screen.v1.ScreenKeyboardEvents;
import net.fabricmc.fabric.api.client.screen.v1.ScreenMouseEvents;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
/**
* Factory methods for creating event instances used in {@link ScreenExtensions}.
*/
@ApiStatus.Internal
@Environment(EnvType.CLIENT)
public final class ScreenEventFactory {
public static Event<ScreenEvents.Remove> createRemoveEvent() {
return EventFactory.createArrayBacked(ScreenEvents.Remove.class, callbacks -> screen -> {
for (ScreenEvents.Remove callback : callbacks) {
callback.onRemove(screen);
}
});
}
public static Event<ScreenEvents.BeforeRender> createBeforeRenderEvent() {
return EventFactory.createArrayBacked(ScreenEvents.BeforeRender.class, callbacks -> (screen, matrices, mouseX, mouseY, tickDelta) -> {
for (ScreenEvents.BeforeRender callback : callbacks) {
callback.beforeRender(screen, matrices, mouseX, mouseY, tickDelta);
}
});
}
public static Event<ScreenEvents.AfterRender> createAfterRenderEvent() {
return EventFactory.createArrayBacked(ScreenEvents.AfterRender.class, callbacks -> (screen, matrices, mouseX, mouseY, tickDelta) -> {
for (ScreenEvents.AfterRender callback : callbacks) {
callback.afterRender(screen, matrices, mouseX, mouseY, tickDelta);
}
});
}
public static Event<ScreenEvents.BeforeTick> createBeforeTickEvent() {
return EventFactory.createArrayBacked(ScreenEvents.BeforeTick.class, callbacks -> screen -> {
for (ScreenEvents.BeforeTick callback : callbacks) {
callback.beforeTick(screen);
}
});
}
public static Event<ScreenEvents.AfterTick> createAfterTickEvent() {
return EventFactory.createArrayBacked(ScreenEvents.AfterTick.class, callbacks -> screen -> {
for (ScreenEvents.AfterTick callback : callbacks) {
callback.afterTick(screen);
}
});
}
// Keyboard events
public static Event<ScreenKeyboardEvents.AllowKeyPress> createAllowKeyPressEvent() {
return EventFactory.createArrayBacked(ScreenKeyboardEvents.AllowKeyPress.class, callbacks -> (screen, key, scancode, modifiers) -> {
for (ScreenKeyboardEvents.AllowKeyPress callback : callbacks) {
if (!callback.allowKeyPress(screen, key, scancode, modifiers)) {
return false;
}
}
return true;
});
}
public static Event<ScreenKeyboardEvents.BeforeKeyPress> createBeforeKeyPressEvent() {
return EventFactory.createArrayBacked(ScreenKeyboardEvents.BeforeKeyPress.class, callbacks -> (screen, key, scancode, modifiers) -> {
for (ScreenKeyboardEvents.BeforeKeyPress callback : callbacks) {
callback.beforeKeyPress(screen, key, scancode, modifiers);
}
});
}
public static Event<ScreenKeyboardEvents.AfterKeyPress> createAfterKeyPressEvent() {
return EventFactory.createArrayBacked(ScreenKeyboardEvents.AfterKeyPress.class, callbacks -> (screen, key, scancode, modifiers) -> {
for (ScreenKeyboardEvents.AfterKeyPress callback : callbacks) {
callback.afterKeyPress(screen, key, scancode, modifiers);
}
});
}
public static Event<ScreenKeyboardEvents.AllowKeyRelease> createAllowKeyReleaseEvent() {
return EventFactory.createArrayBacked(ScreenKeyboardEvents.AllowKeyRelease.class, callbacks -> (screen, key, scancode, modifiers) -> {
for (ScreenKeyboardEvents.AllowKeyRelease callback : callbacks) {
if (!callback.allowKeyRelease(screen, key, scancode, modifiers)) {
return false;
}
}
return true;
});
}
public static Event<ScreenKeyboardEvents.BeforeKeyRelease> createBeforeKeyReleaseEvent() {
return EventFactory.createArrayBacked(ScreenKeyboardEvents.BeforeKeyRelease.class, callbacks -> (screen, key, scancode, modifiers) -> {
for (ScreenKeyboardEvents.BeforeKeyRelease callback : callbacks) {
callback.beforeKeyRelease(screen, key, scancode, modifiers);
}
});
}
public static Event<ScreenKeyboardEvents.AfterKeyRelease> createAfterKeyReleaseEvent() {
return EventFactory.createArrayBacked(ScreenKeyboardEvents.AfterKeyRelease.class, callbacks -> (screen, key, scancode, modifiers) -> {
for (ScreenKeyboardEvents.AfterKeyRelease callback : callbacks) {
callback.afterKeyRelease(screen, key, scancode, modifiers);
}
});
}
// Mouse Events
public static Event<ScreenMouseEvents.AllowMouseClick> createAllowMouseClickEvent() {
return EventFactory.createArrayBacked(ScreenMouseEvents.AllowMouseClick.class, callbacks -> (screen, mouseX, mouseY, button) -> {
for (ScreenMouseEvents.AllowMouseClick callback : callbacks) {
if (!callback.allowMouseClick(screen, mouseX, mouseY, button)) {
return false;
}
}
return true;
});
}
public static Event<ScreenMouseEvents.BeforeMouseClick> createBeforeMouseClickEvent() {
return EventFactory.createArrayBacked(ScreenMouseEvents.BeforeMouseClick.class, callbacks -> (screen, mouseX, mouseY, button) -> {
for (ScreenMouseEvents.BeforeMouseClick callback : callbacks) {
callback.beforeMouseClick(screen, mouseX, mouseY, button);
}
});
}
public static Event<ScreenMouseEvents.AfterMouseClick> createAfterMouseClickEvent() {
return EventFactory.createArrayBacked(ScreenMouseEvents.AfterMouseClick.class, callbacks -> (screen, mouseX, mouseY, button) -> {
for (ScreenMouseEvents.AfterMouseClick callback : callbacks) {
callback.afterMouseClick(screen, mouseX, mouseY, button);
}
});
}
public static Event<ScreenMouseEvents.AllowMouseRelease> createAllowMouseReleaseEvent() {
return EventFactory.createArrayBacked(ScreenMouseEvents.AllowMouseRelease.class, callbacks -> (screen, mouseX, mouseY, button) -> {
for (ScreenMouseEvents.AllowMouseRelease callback : callbacks) {
if (!callback.allowMouseRelease(screen, mouseX, mouseY, button)) {
return false;
}
}
return true;
});
}
public static Event<ScreenMouseEvents.BeforeMouseRelease> createBeforeMouseReleaseEvent() {
return EventFactory.createArrayBacked(ScreenMouseEvents.BeforeMouseRelease.class, callbacks -> (screen, mouseX, mouseY, button) -> {
for (ScreenMouseEvents.BeforeMouseRelease callback : callbacks) {
callback.beforeMouseRelease(screen, mouseX, mouseY, button);
}
});
}
public static Event<ScreenMouseEvents.AfterMouseRelease> createAfterMouseReleaseEvent() {
return EventFactory.createArrayBacked(ScreenMouseEvents.AfterMouseRelease.class, callbacks -> (screen, mouseX, mouseY, button) -> {
for (ScreenMouseEvents.AfterMouseRelease callback : callbacks) {
callback.afterMouseRelease(screen, mouseX, mouseY, button);
}
});
}
public static Event<ScreenMouseEvents.AllowMouseScroll> createAllowMouseScrollEvent() {
return EventFactory.createArrayBacked(ScreenMouseEvents.AllowMouseScroll.class, callbacks -> (screen, mouseX, mouseY, horizontalAmount, verticalAmount) -> {
for (ScreenMouseEvents.AllowMouseScroll callback : callbacks) {
if (!callback.allowMouseScroll(screen, mouseX, mouseY, horizontalAmount, verticalAmount)) {
return false;
}
}
return true;
});
}
public static Event<ScreenMouseEvents.BeforeMouseScroll> createBeforeMouseScrollEvent() {
return EventFactory.createArrayBacked(ScreenMouseEvents.BeforeMouseScroll.class, callbacks -> (screen, mouseX, mouseY, horizontalAmount, verticalAmount) -> {
for (ScreenMouseEvents.BeforeMouseScroll callback : callbacks) {
callback.beforeMouseScroll(screen, mouseX, mouseY, horizontalAmount, verticalAmount);
}
});
}
public static Event<ScreenMouseEvents.AfterMouseScroll> createAfterMouseScrollEvent() {
return EventFactory.createArrayBacked(ScreenMouseEvents.AfterMouseScroll.class, callbacks -> (screen, mouseX, mouseY, horizontalAmount, verticalAmount) -> {
for (ScreenMouseEvents.AfterMouseScroll callback : callbacks) {
callback.afterMouseScroll(screen, mouseX, mouseY, horizontalAmount, verticalAmount);
}
});
}
private ScreenEventFactory() {
}
}

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.client.screen;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
import net.fabricmc.fabric.api.client.screen.v1.ScreenKeyboardEvents;
import net.fabricmc.fabric.api.client.screen.v1.ScreenMouseEvents;
import net.fabricmc.fabric.api.event.Event;
@ApiStatus.Internal
@Environment(EnvType.CLIENT)
public interface ScreenExtensions {
static ScreenExtensions getExtensions(Screen screen) {
return (ScreenExtensions) screen;
}
List<AbstractButtonWidget> fabric_getButtons();
Event<ScreenEvents.Remove> fabric_getRemoveEvent();
Event<ScreenEvents.BeforeTick> fabric_getBeforeTickEvent();
Event<ScreenEvents.AfterTick> fabric_getAfterTickEvent();
Event<ScreenEvents.BeforeRender> fabric_getBeforeRenderEvent();
Event<ScreenEvents.AfterRender> fabric_getAfterRenderEvent();
// Keyboard
Event<ScreenKeyboardEvents.AllowKeyPress> fabric_getAllowKeyPressEvent();
Event<ScreenKeyboardEvents.BeforeKeyPress> fabric_getBeforeKeyPressEvent();
Event<ScreenKeyboardEvents.AfterKeyPress> fabric_getAfterKeyPressEvent();
Event<ScreenKeyboardEvents.AllowKeyRelease> fabric_getAllowKeyReleaseEvent();
Event<ScreenKeyboardEvents.BeforeKeyRelease> fabric_getBeforeKeyReleaseEvent();
Event<ScreenKeyboardEvents.AfterKeyRelease> fabric_getAfterKeyReleaseEvent();
// Mouse
Event<ScreenMouseEvents.AllowMouseClick> fabric_getAllowMouseClickEvent();
Event<ScreenMouseEvents.BeforeMouseClick> fabric_getBeforeMouseClickEvent();
Event<ScreenMouseEvents.AfterMouseClick> fabric_getAfterMouseClickEvent();
Event<ScreenMouseEvents.AllowMouseRelease> fabric_getAllowMouseReleaseEvent();
Event<ScreenMouseEvents.BeforeMouseRelease> fabric_getBeforeMouseReleaseEvent();
Event<ScreenMouseEvents.AfterMouseRelease> fabric_getAfterMouseReleaseEvent();
Event<ScreenMouseEvents.AllowMouseScroll> fabric_getAllowMouseScrollEvent();
Event<ScreenMouseEvents.BeforeMouseScroll> fabric_getBeforeMouseScrollEvent();
Event<ScreenMouseEvents.AfterMouseScroll> fabric_getAfterMouseScrollEvent();
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.screen;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
@Environment(EnvType.CLIENT)
@Mixin(GameRenderer.class)
abstract class GameRendererMixin {
@Shadow
@Final
private MinecraftClient client;
@Unique
private Screen renderingScreen;
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;render(Lnet/minecraft/client/util/math/MatrixStack;IIF)V"), locals = LocalCapture.CAPTURE_FAILEXCEPTION)
private void onBeforeRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo ci, int mouseX, int mouseY, MatrixStack matrices) {
// Store the screen in a variable in case someone tries to change the screen during this before render event.
// If someone changes the screen, the after render event will likely have class cast exceptions or an NPE.
this.renderingScreen = this.client.currentScreen;
ScreenEvents.beforeRender(this.renderingScreen).invoker().beforeRender(this.renderingScreen, matrices, mouseX, mouseY, tickDelta);
}
// This injection should end up in the try block so exceptions are caught
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;render(Lnet/minecraft/client/util/math/MatrixStack;IIF)V", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILEXCEPTION)
private void onAfterRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo ci, int mouseX, int mouseY, MatrixStack matrices) {
ScreenEvents.afterRender(this.renderingScreen).invoker().afterRender(this.renderingScreen, matrices, mouseX, mouseY, tickDelta);
// Finally set the currently rendering screen to null
this.renderingScreen = null;
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.screen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.Keyboard;
import net.minecraft.client.gui.ParentElement;
import net.minecraft.client.gui.screen.Screen;
import net.fabricmc.fabric.api.client.screen.v1.ScreenKeyboardEvents;
@Mixin(Keyboard.class)
abstract class KeyboardMixin {
// private synthetic method_1454(I[ZLnet/minecraft/client/gui/ParentElement;III)V
@Inject(method = "method_1454(I[ZLnet/minecraft/client/gui/ParentElement;III)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/ParentElement;keyPressed(III)Z"), cancellable = true)
private void beforeKeyPressedEvent(int code, boolean[] resultHack, ParentElement parentElement, int key, int scancode, int modifiers, CallbackInfo ci) {
final Screen screen = (Screen) parentElement;
if (!ScreenKeyboardEvents.allowKeyPress(screen).invoker().allowKeyPress(screen, key, scancode, modifiers)) {
resultHack[0] = true; // Set this press action as handled.
ci.cancel(); // Exit the lambda
return;
}
ScreenKeyboardEvents.beforeKeyPress(screen).invoker().beforeKeyPress(screen, key, scancode, modifiers);
}
// private synthetic method_1454(I[ZLnet/minecraft/client/gui/ParentElement;III)V
@Inject(method = "method_1454(I[ZLnet/minecraft/client/gui/ParentElement;III)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/ParentElement;keyPressed(III)Z", shift = At.Shift.AFTER))
private void afterKeyPressedEvent(int code, boolean[] resultHack, ParentElement parentElement, int key, int scancode, int modifiers, CallbackInfo ci) {
final Screen screen = (Screen) parentElement;
ScreenKeyboardEvents.afterKeyPress(screen).invoker().afterKeyPress(screen, key, scancode, modifiers);
}
// private synthetic method_1454(I[ZLnet/minecraft/client/gui/ParentElement;III)V
@Inject(method = "method_1454(I[ZLnet/minecraft/client/gui/ParentElement;III)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/ParentElement;keyReleased(III)Z"), cancellable = true)
private void beforeKeyReleasedEvent(int code, boolean[] resultHack, ParentElement parentElement, int key, int scancode, int modifiers, CallbackInfo ci) {
final Screen screen = (Screen) parentElement;
if (!ScreenKeyboardEvents.allowKeyRelease(screen).invoker().allowKeyRelease(screen, key, scancode, modifiers)) {
resultHack[0] = true; // Set this press action as handled.
ci.cancel(); // Exit the lambda
return;
}
ScreenKeyboardEvents.beforeKeyRelease(screen).invoker().beforeKeyRelease(screen, key, scancode, modifiers);
}
// private synthetic method_1454(I[ZLnet/minecraft/client/gui/ParentElement;III)V
@Inject(method = "method_1454(I[ZLnet/minecraft/client/gui/ParentElement;III)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/ParentElement;keyReleased(III)Z", shift = At.Shift.AFTER))
private void afterKeyReleasedEvent(int code, boolean[] resultHack, ParentElement parentElement, int key, int scancode, int modifiers, CallbackInfo ci) {
final Screen screen = (Screen) parentElement;
ScreenKeyboardEvents.afterKeyRelease(screen).invoker().afterKeyRelease(screen, key, scancode, modifiers);
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.screen;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
@Mixin(MinecraftClient.class)
abstract class MinecraftClientMixin {
@Shadow
public Screen currentScreen;
@Unique
private Screen tickingScreen;
@Inject(method = "openScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;removed()V", shift = At.Shift.AFTER))
private void onScreenRemove(@Nullable Screen screen, CallbackInfo ci) {
ScreenEvents.remove(this.currentScreen).invoker().onRemove(this.currentScreen);
}
@Inject(method = "stop", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;removed()V", shift = At.Shift.AFTER))
private void onScreenRemoveBecauseStopping(CallbackInfo ci) {
ScreenEvents.remove(this.currentScreen).invoker().onRemove(this.currentScreen);
}
// Synthetic method in `tick`
// These two injections should be caught by "Screen#wrapScreenError" if anything fails in an event and then rethrown in the crash report
@Inject(method = "method_1572", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;tick()V"))
private void beforeScreenTick(CallbackInfo ci) {
// Store the screen in a variable in case someone tries to change the screen during this before tick event.
// If someone changes the screen, the after tick event will likely have class cast exceptions or an NPE.
this.tickingScreen = this.currentScreen;
ScreenEvents.beforeTick(this.tickingScreen).invoker().beforeTick(this.tickingScreen);
}
// Synthetic method in `tick`
@Inject(method = "method_1572", at = @At("TAIL"))
private void afterScreenTick(CallbackInfo ci) {
ScreenEvents.afterTick(this.tickingScreen).invoker().afterTick(this.tickingScreen);
// Finally set the currently ticking screen to null
this.tickingScreen = null;
}
// The LevelLoadingScreen is the odd screen that isn't ticked by the main tick loop, so we fire events for this screen.
// We Coerce the package-private inner class representing the world load action so we don't need an access widener.
@Inject(method = "startIntegratedServer(Ljava/lang/String;Lnet/minecraft/util/registry/DynamicRegistryManager$Impl;Ljava/util/function/Function;Lcom/mojang/datafixers/util/Function4;ZLnet/minecraft/client/MinecraftClient$WorldLoadAction;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/LevelLoadingScreen;tick()V"))
private void beforeLoadingScreenTick(CallbackInfo ci) {
// Store the screen in a variable in case someone tries to change the screen during this before tick event.
// If someone changes the screen, the after tick event will likely have class cast exceptions or throw a NPE.
this.tickingScreen = this.currentScreen;
ScreenEvents.beforeTick(this.tickingScreen).invoker().beforeTick(this.tickingScreen);
}
@Inject(method = "startIntegratedServer(Ljava/lang/String;Lnet/minecraft/util/registry/DynamicRegistryManager$Impl;Ljava/util/function/Function;Lcom/mojang/datafixers/util/Function4;ZLnet/minecraft/client/MinecraftClient$WorldLoadAction;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;render(Z)V"))
private void afterLoadingScreenTick(CallbackInfo ci) {
ScreenEvents.afterTick(this.tickingScreen).invoker().afterTick(this.tickingScreen);
// Finally set the currently ticking screen to null
this.tickingScreen = null;
}
}

View file

@ -0,0 +1,141 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.screen;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.Mouse;
import net.minecraft.client.gui.screen.Screen;
import net.fabricmc.fabric.api.client.screen.v1.ScreenMouseEvents;
@Mixin(Mouse.class)
abstract class MouseMixin {
@Shadow
@Final
private MinecraftClient client;
@Unique
private Screen currentScreen;
@Unique
private Double horizontalScrollAmount;
// private synthetic method_1611([ZDDI)V
@Inject(method = "method_1611([ZDDI)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseClicked(DDI)Z"), cancellable = true)
private void beforeMouseClickedEvent(boolean[] resultHack, double mouseX, double mouseY, int button, CallbackInfo ci) {
// Store the screen in a variable in case someone tries to change the screen during this before event.
// If someone changes the screen, the after event will likely have class cast exceptions or throw a NPE.
this.currentScreen = this.client.currentScreen;
if (this.currentScreen == null) {
return;
}
if (!ScreenMouseEvents.allowMouseClick(this.currentScreen).invoker().allowMouseClick(this.currentScreen, mouseX, mouseY, button)) {
resultHack[0] = true; // Set this press action as handled.
this.currentScreen = null;
ci.cancel(); // Exit the lambda
return;
}
ScreenMouseEvents.beforeMouseClick(this.currentScreen).invoker().beforeMouseClick(this.currentScreen, mouseX, mouseY, button);
}
// private synthetic method_1611([ZDDI)V
@Inject(method = "method_1611([ZDDI)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseClicked(DDI)Z", shift = At.Shift.AFTER))
private void afterMouseClickedEvent(boolean[] resultHack, double mouseX, double mouseY, int button, CallbackInfo ci) {
if (this.currentScreen == null) {
return;
}
ScreenMouseEvents.afterMouseClick(this.currentScreen).invoker().afterMouseClick(this.currentScreen, mouseX, mouseY, button);
this.currentScreen = null;
}
// private synthetic method_1605([ZDDI)V
@Inject(method = "method_1605([ZDDI)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseReleased(DDI)Z"), cancellable = true)
private void beforeMouseReleasedEvent(boolean[] resultHack, double mouseX, double mouseY, int button, CallbackInfo ci) {
// Store the screen in a variable in case someone tries to change the screen during this before event.
// If someone changes the screen, the after event will likely have class cast exceptions or throw a NPE.
this.currentScreen = this.client.currentScreen;
if (this.currentScreen == null) {
return;
}
if (!ScreenMouseEvents.allowMouseRelease(this.currentScreen).invoker().allowMouseRelease(this.currentScreen, mouseX, mouseY, button)) {
resultHack[0] = true; // Set this press action as handled.
this.currentScreen = null;
ci.cancel(); // Exit the lambda
return;
}
ScreenMouseEvents.beforeMouseRelease(this.currentScreen).invoker().beforeMouseRelease(this.currentScreen, mouseX, mouseY, button);
}
// private synthetic method_1605([ZDDI)V
@Inject(method = "method_1605([ZDDI)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseReleased(DDI)Z", shift = At.Shift.AFTER))
private void afterMouseReleasedEvent(boolean[] resultHack, double mouseX, double mouseY, int button, CallbackInfo ci) {
if (this.currentScreen == null) {
return;
}
ScreenMouseEvents.afterMouseRelease(this.currentScreen).invoker().afterMouseRelease(this.currentScreen, mouseX, mouseY, button);
this.currentScreen = null;
}
@Inject(method = "onMouseScroll", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseScrolled(DDD)Z"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true)
private void beforeMouseScrollEvent(long window, double horizontal, double vertical, CallbackInfo ci, double verticalAmount, double mouseX, double mouseY) {
// Store the screen in a variable in case someone tries to change the screen during this before event.
// If someone changes the screen, the after event will likely have class cast exceptions or throw a NPE.
this.currentScreen = this.client.currentScreen;
if (this.currentScreen == null) {
return;
}
// Apply same calculations to horizontal scroll as vertical scroll amount has
this.horizontalScrollAmount = this.client.options.discreteMouseScroll ? Math.signum(horizontal) : horizontal * this.client.options.mouseWheelSensitivity;
if (!ScreenMouseEvents.allowMouseScroll(this.currentScreen).invoker().allowMouseScroll(this.currentScreen, mouseX, mouseY, this.horizontalScrollAmount, verticalAmount)) {
this.currentScreen = null;
this.horizontalScrollAmount = null;
ci.cancel();
return;
}
ScreenMouseEvents.beforeMouseScroll(this.currentScreen).invoker().beforeMouseScroll(this.currentScreen, mouseX, mouseY, this.horizontalScrollAmount, verticalAmount);
}
@Inject(method = "onMouseScroll", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseScrolled(DDD)Z", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILEXCEPTION)
private void afterMouseScrollEvent(long window, double horizontal, double vertical, CallbackInfo ci, double verticalAmount, double mouseX, double mouseY) {
if (this.currentScreen == null) {
return;
}
ScreenMouseEvents.afterMouseScroll(this.currentScreen).invoker().afterMouseScroll(this.currentScreen, mouseX, mouseY, this.horizontalScrollAmount, verticalAmount);
this.currentScreen = null;
this.horizontalScrollAmount = null;
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.screen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.render.item.ItemRenderer;
@Mixin(Screen.class)
public interface ScreenAccessor {
@Accessor
ItemRenderer getItemRenderer();
@Accessor
TextRenderer getTextRenderer();
@Accessor
MinecraftClient getClient();
}

View file

@ -0,0 +1,248 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.screen;
import java.util.List;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
import net.fabricmc.fabric.impl.client.screen.ScreenExtensions;
import net.fabricmc.fabric.api.client.screen.v1.ScreenKeyboardEvents;
import net.fabricmc.fabric.api.client.screen.v1.ScreenMouseEvents;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.impl.client.screen.ButtonList;
import net.fabricmc.fabric.impl.client.screen.ScreenEventFactory;
@Mixin(Screen.class)
abstract class ScreenMixin implements ScreenExtensions {
@Shadow
@Final
protected List<AbstractButtonWidget> buttons;
@Shadow
@Final
protected List<Element> children;
@Unique
private ButtonList<AbstractButtonWidget> fabricButtons;
@Unique
private Event<ScreenEvents.Remove> removeEvent;
@Unique
private Event<ScreenEvents.BeforeTick> beforeTickEvent;
@Unique
private Event<ScreenEvents.AfterTick> afterTickEvent;
@Unique
private Event<ScreenEvents.BeforeRender> beforeRenderEvent;
@Unique
private Event<ScreenEvents.AfterRender> afterRenderEvent;
// Keyboard
@Unique
private Event<ScreenKeyboardEvents.AllowKeyPress> allowKeyPressEvent;
@Unique
private Event<ScreenKeyboardEvents.BeforeKeyPress> beforeKeyPressEvent;
@Unique
private Event<ScreenKeyboardEvents.AfterKeyPress> afterKeyPressEvent;
@Unique
private Event<ScreenKeyboardEvents.AllowKeyRelease> allowKeyReleaseEvent;
@Unique
private Event<ScreenKeyboardEvents.BeforeKeyRelease> beforeKeyReleaseEvent;
@Unique
private Event<ScreenKeyboardEvents.AfterKeyRelease> afterKeyReleaseEvent;
// Mouse
@Unique
private Event<ScreenMouseEvents.AllowMouseClick> allowMouseClickEvent;
@Unique
private Event<ScreenMouseEvents.BeforeMouseClick> beforeMouseClickEvent;
@Unique
private Event<ScreenMouseEvents.AfterMouseClick> afterMouseClickEvent;
@Unique
private Event<ScreenMouseEvents.AllowMouseRelease> allowMouseReleaseEvent;
@Unique
private Event<ScreenMouseEvents.BeforeMouseRelease> beforeMouseReleaseEvent;
@Unique
private Event<ScreenMouseEvents.AfterMouseRelease> afterMouseReleaseEvent;
@Unique
private Event<ScreenMouseEvents.AllowMouseScroll> allowMouseScrollEvent;
@Unique
private Event<ScreenMouseEvents.BeforeMouseScroll> beforeMouseScrollEvent;
@Unique
private Event<ScreenMouseEvents.AfterMouseScroll> afterMouseScrollEvent;
@Inject(method = "init(Lnet/minecraft/client/MinecraftClient;II)V", at = @At("HEAD"))
private void beforeInitScreen(MinecraftClient client, int width, int height, CallbackInfo ci) {
// All elements are repopulated on the screen, so we need to reinitialize all events
this.fabricButtons = null;
this.removeEvent = ScreenEventFactory.createRemoveEvent();
this.beforeRenderEvent = ScreenEventFactory.createBeforeRenderEvent();
this.afterRenderEvent = ScreenEventFactory.createAfterRenderEvent();
this.beforeTickEvent = ScreenEventFactory.createBeforeTickEvent();
this.afterTickEvent = ScreenEventFactory.createAfterTickEvent();
// Keyboard
this.allowKeyPressEvent = ScreenEventFactory.createAllowKeyPressEvent();
this.beforeKeyPressEvent = ScreenEventFactory.createBeforeKeyPressEvent();
this.afterKeyPressEvent = ScreenEventFactory.createAfterKeyPressEvent();
this.allowKeyReleaseEvent = ScreenEventFactory.createAllowKeyReleaseEvent();
this.beforeKeyReleaseEvent = ScreenEventFactory.createBeforeKeyReleaseEvent();
this.afterKeyReleaseEvent = ScreenEventFactory.createAfterKeyReleaseEvent();
// Mouse
this.allowMouseClickEvent = ScreenEventFactory.createAllowMouseClickEvent();
this.beforeMouseClickEvent = ScreenEventFactory.createBeforeMouseClickEvent();
this.afterMouseClickEvent = ScreenEventFactory.createAfterMouseClickEvent();
this.allowMouseReleaseEvent = ScreenEventFactory.createAllowMouseReleaseEvent();
this.beforeMouseReleaseEvent = ScreenEventFactory.createBeforeMouseReleaseEvent();
this.afterMouseReleaseEvent = ScreenEventFactory.createAfterMouseReleaseEvent();
this.allowMouseScrollEvent = ScreenEventFactory.createAllowMouseScrollEvent();
this.beforeMouseScrollEvent = ScreenEventFactory.createBeforeMouseScrollEvent();
this.afterMouseScrollEvent = ScreenEventFactory.createAfterMouseScrollEvent();
ScreenEvents.BEFORE_INIT.invoker().beforeInit(client, (Screen) (Object) this, width, height);
}
@Inject(method = "init(Lnet/minecraft/client/MinecraftClient;II)V", at = @At("TAIL"))
private void afterInitScreen(MinecraftClient client, int width, int height, CallbackInfo ci) {
ScreenEvents.AFTER_INIT.invoker().afterInit(client, (Screen) (Object) this, width, height);
}
@Override
public List<AbstractButtonWidget> fabric_getButtons() {
// Lazy init to make the list access safe after Screen#init
if (this.fabricButtons == null) {
this.fabricButtons = new ButtonList<>((Screen) (Object) this, this.buttons, this.children);
}
return this.fabricButtons;
}
@Override
public Event<ScreenEvents.Remove> fabric_getRemoveEvent() {
return this.removeEvent;
}
@Override
public Event<ScreenEvents.BeforeTick> fabric_getBeforeTickEvent() {
return this.beforeTickEvent;
}
@Override
public Event<ScreenEvents.AfterTick> fabric_getAfterTickEvent() {
return this.afterTickEvent;
}
@Override
public Event<ScreenEvents.BeforeRender> fabric_getBeforeRenderEvent() {
return this.beforeRenderEvent;
}
@Override
public Event<ScreenEvents.AfterRender> fabric_getAfterRenderEvent() {
return this.afterRenderEvent;
}
// Keyboard
@Override
public Event<ScreenKeyboardEvents.AllowKeyPress> fabric_getAllowKeyPressEvent() {
return this.allowKeyPressEvent;
}
@Override
public Event<ScreenKeyboardEvents.BeforeKeyPress> fabric_getBeforeKeyPressEvent() {
return this.beforeKeyPressEvent;
}
@Override
public Event<ScreenKeyboardEvents.AfterKeyPress> fabric_getAfterKeyPressEvent() {
return this.afterKeyPressEvent;
}
@Override
public Event<ScreenKeyboardEvents.AllowKeyRelease> fabric_getAllowKeyReleaseEvent() {
return this.allowKeyReleaseEvent;
}
@Override
public Event<ScreenKeyboardEvents.BeforeKeyRelease> fabric_getBeforeKeyReleaseEvent() {
return this.beforeKeyReleaseEvent;
}
@Override
public Event<ScreenKeyboardEvents.AfterKeyRelease> fabric_getAfterKeyReleaseEvent() {
return this.afterKeyReleaseEvent;
}
// Mouse
@Override
public Event<ScreenMouseEvents.AllowMouseClick> fabric_getAllowMouseClickEvent() {
return this.allowMouseClickEvent;
}
@Override
public Event<ScreenMouseEvents.BeforeMouseClick> fabric_getBeforeMouseClickEvent() {
return this.beforeMouseClickEvent;
}
@Override
public Event<ScreenMouseEvents.AfterMouseClick> fabric_getAfterMouseClickEvent() {
return this.afterMouseClickEvent;
}
@Override
public Event<ScreenMouseEvents.AllowMouseRelease> fabric_getAllowMouseReleaseEvent() {
return this.allowMouseReleaseEvent;
}
@Override
public Event<ScreenMouseEvents.BeforeMouseRelease> fabric_getBeforeMouseReleaseEvent() {
return this.beforeMouseReleaseEvent;
}
@Override
public Event<ScreenMouseEvents.AfterMouseRelease> fabric_getAfterMouseReleaseEvent() {
return this.afterMouseReleaseEvent;
}
@Override
public Event<ScreenMouseEvents.AllowMouseScroll> fabric_getAllowMouseScrollEvent() {
return this.allowMouseScrollEvent;
}
@Override
public Event<ScreenMouseEvents.BeforeMouseScroll> fabric_getBeforeMouseScrollEvent() {
return this.beforeMouseScrollEvent;
}
@Override
public Event<ScreenMouseEvents.AfterMouseScroll> fabric_getAfterMouseScrollEvent() {
return this.afterMouseScrollEvent;
}
}

Binary file not shown.

After

(image error) Size: 1.5 KiB

View file

@ -0,0 +1,18 @@
{
"required": true,
"package": "net.fabricmc.fabric.mixin.screen",
"compatibilityLevel": "JAVA_8",
"client": [
"GameRendererMixin",
"MinecraftClientMixin",
"ScreenMixin"
],
"injectors": {
"defaultRequire": 1
},
"mixins": [
"KeyboardMixin",
"MouseMixin",
"ScreenAccessor"
]
}

View file

@ -0,0 +1,29 @@
{
"schemaVersion": 1,
"id": "fabric-screen-api-v1",
"name": "Fabric Screen API (v1)",
"version": "${version}",
"environment": "client",
"license": "Apache-2.0",
"icon": "assets/fabric-screen-api-v1/icon.png",
"contact": {
"homepage": "https://fabricmc.net",
"irc": "irc://irc.esper.net:6667/fabric",
"issues": "https://github.com/FabricMC/fabric/issues",
"sources": "https://github.com/FabricMC/fabric"
},
"authors": [
"FabricMC"
],
"depends": {
"fabricloader": ">=0.8.2",
"fabric-api-base": "*"
},
"description": "Adds screen related hooks.",
"mixins": [
"fabric-screen-api-v1.mixins.json"
],
"custom": {
"fabric-api:module-lifecycle": "stable"
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.screen;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
import net.fabricmc.fabric.api.client.screen.v1.ScreenKeyboardEvents;
import net.fabricmc.fabric.api.client.screen.v1.Screens;
@Environment(EnvType.CLIENT)
public final class ScreenTests implements ClientModInitializer {
private static final Logger LOGGER = LogManager.getLogger("FabricScreenApiTests");
@Override
public void onInitializeClient() {
LOGGER.info("Started Screen Testmod");
ScreenEvents.BEFORE_INIT.register((client, screen, width, height) -> {
// TODO: Write tests listening to addition of child elements
});
ScreenEvents.AFTER_INIT.register(this::afterInitScreen);
}
private void afterInitScreen(MinecraftClient client, Screen screen, int windowWidth, int windowHeight) {
LOGGER.info("Initializing {}", screen.getClass().getName());
if (screen instanceof TitleScreen) {
final List<AbstractButtonWidget> buttons = Screens.getButtons(screen);
// Shrink the realms button, should be the third button on the list
final AbstractButtonWidget optionsButton = buttons.get(2);
optionsButton.setWidth(98);
// Add a new button
buttons.add(new SoundButton((screen.width / 2) + 2, ((screen.height / 4) + 96), 72, 20));
// And another button
buttons.add(new StopSoundButton(screen, (screen.width / 2) + 80, ((screen.height / 4) + 95), 20, 20));
// Testing:
// Some automatic validation that the screen list works, make sure the buttons we added are on the list of child elements
screen.children().stream()
.filter(element -> element instanceof SoundButton)
.findAny()
.orElseThrow(() -> new AssertionError("Failed to find the \"Sound\" button in the screen's elements"));
screen.children().stream()
.filter(element -> element instanceof StopSoundButton)
.findAny()
.orElseThrow(() -> new AssertionError("Failed to find the \"Stop Sound\" button in the screen's elements"));
// Register render event to draw an icon on the screen
ScreenEvents.afterRender(screen).register((_screen, matrices, mouseX, mouseY, tickDelta) -> {
// Render an armor icon to test
client.getTextureManager().bindTexture(InGameHud.GUI_ICONS_TEXTURE);
DrawableHelper.drawTexture(matrices, (screen.width / 2) - 124, (screen.height / 4) + 96, 20, 20, 34, 9, 9, 9, 256, 256);
});
ScreenKeyboardEvents.allowKeyPress(screen).register((_screen, key, scancode, modifiers) -> {
LOGGER.info("After Pressed, Code: {}, Scancode: {}, Modifiers: {}", key, scancode, modifiers);
return false; // Let actions continue
});
ScreenKeyboardEvents.afterKeyPress(screen).register((_screen, key, scancode, modifiers) -> {
LOGGER.warn("Pressed, Code: {}, Scancode: {}, Modifiers: {}", key, scancode, modifiers);
});
}
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.screen;
import java.util.Random;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.widget.AbstractPressableButtonWidget;
import net.minecraft.client.sound.PositionedSoundInstance;
import net.minecraft.sound.SoundEvent;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.SimpleRegistry;
class SoundButton extends AbstractPressableButtonWidget {
private static final Random RANDOM = new Random();
SoundButton(int x, int y, int width, int height) {
super(x, y, width, height, Text.of("Sound Button"));
}
@Override
public void onPress() {
// Upcast on registry is fine
@Nullable
final SoundEvent event = ((SimpleRegistry<SoundEvent>) Registry.SOUND_EVENT).getRandom(RANDOM);
MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(event != null ? event : SoundEvents.ENTITY_GENERIC_EXPLODE, 1.0F, 1.0F));
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.screen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.AbstractPressableButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
class StopSoundButton extends AbstractPressableButtonWidget {
private final Screen screen;
StopSoundButton(Screen screen, int x, int y, int width, int height) {
super(x, y, width, height, Text.of(""));
this.screen = screen;
}
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float tickDelta) {
MinecraftClient client = MinecraftClient.getInstance();
// Render the armor icon to test
client.getTextureManager().bindTexture(InGameHud.GUI_ICONS_TEXTURE);
DrawableHelper.drawTexture(matrices, this.x, this.y, this.width, this.height, 43, 27, 9, 9, 256, 256);
if (this.isMouseOver(mouseX, mouseY)) {
this.screen.renderTooltip(matrices, new LiteralText("Click to stop all sounds"), this.x, this.y);
}
}
@Override
public void onPress() {
MinecraftClient.getInstance().getSoundManager().stopAll();
}
}

View file

@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"id": "fabric-screen-api-v1-testmod",
"name": "Fabric Screen API (v1) Test Mod",
"version": "1.0.0",
"environment": "client",
"license": "Apache-2.0",
"depends": {
"fabric-screen-api-v1": "*"
},
"entrypoints": {
"client": [
"net.fabricmc.fabric.test.screen.ScreenTests"
]
}
}

View file

@ -49,6 +49,7 @@ include 'fabric-rendering-v1'
include 'fabric-rendering-data-attachment-v1'
include 'fabric-rendering-fluids-v1'
include 'fabric-resource-loader-v0'
include 'fabric-screen-api-v1'
include 'fabric-screen-handler-api-v1'
include 'fabric-structure-api-v1'
include 'fabric-tag-extensions-v0'