1
0
forked from Clones/Controlify

button guide hud

This commit is contained in:
isXander
2023-02-09 23:08:28 +00:00
parent ab801e37b4
commit 773aebe04e
60 changed files with 566 additions and 154 deletions

View File

@ -0,0 +1,162 @@
package dev.isxander.controlify.screenop;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.screenop.component.ComponentProcessorProvider;
import dev.isxander.controlify.screenop.component.CustomFocus;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.event.ControlifyEvents;
import dev.isxander.controlify.mixins.compat.screenop.vanilla.ScreenAccessor;
import net.minecraft.client.gui.ComponentPath;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.navigation.FocusNavigationEvent;
import net.minecraft.client.gui.navigation.ScreenDirection;
import net.minecraft.client.gui.screens.Screen;
import org.lwjgl.glfw.GLFW;
import java.util.*;
public class ScreenProcessor<T extends Screen> {
public final T screen;
protected int lastMoved = 0;
public ScreenProcessor(T screen) {
this.screen = screen;
ControlifyEvents.VIRTUAL_MOUSE_TOGGLED.register(this::onVirtualMouseToggled);
}
public void onControllerUpdate(Controller controller) {
if (!Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) {
handleComponentNavigation(controller);
handleButtons(controller);
} else {
handleVMouseNavigation(controller);
}
}
public void onInputModeChanged(InputMode mode) {
switch (mode) {
case KEYBOARD_MOUSE -> ((ScreenAccessor) screen).invokeClearFocus();
case CONTROLLER -> {
if (!Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) {
setInitialFocus();
}
}
}
}
protected void handleComponentNavigation(Controller controller) {
if (screen.getFocused() == null)
setInitialFocus();
var focusTree = getFocusTree();
var focuses = List.copyOf(focusTree);
while (!focusTree.isEmpty()) {
var focused = focusTree.poll();
var processor = ComponentProcessorProvider.provide(focused);
if (processor.overrideControllerNavigation(this, controller)) return;
}
var accessor = (ScreenAccessor) screen;
boolean repeatEventAvailable = ++lastMoved >= controller.config().screenRepeatNavigationDelay;
var axes = controller.state().axes();
var prevAxes = controller.prevState().axes();
var buttons = controller.state().buttons();
var prevButtons = controller.prevState().buttons();
FocusNavigationEvent.ArrowNavigation event = null;
if (axes.leftStickX() > 0.5f && (repeatEventAvailable || prevAxes.leftStickX() <= 0.5f)) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.RIGHT);
} else if (axes.leftStickX() < -0.5f && (repeatEventAvailable || prevAxes.leftStickX() >= -0.5f)) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.LEFT);
} else if (axes.leftStickY() < -0.5f && (repeatEventAvailable || prevAxes.leftStickY() >= -0.5f)) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.UP);
} else if (axes.leftStickY() > 0.5f && (repeatEventAvailable || prevAxes.leftStickY() <= 0.5f)) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.DOWN);
} else if (buttons.dpadUp() && (repeatEventAvailable || !prevButtons.dpadUp())) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.UP);
} else if (buttons.dpadDown() && (repeatEventAvailable || !prevButtons.dpadDown())) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.DOWN);
} else if (buttons.dpadLeft() && (repeatEventAvailable || !prevButtons.dpadLeft())) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.LEFT);
} else if (buttons.dpadRight() && (repeatEventAvailable || !prevButtons.dpadRight())) {
event = accessor.invokeCreateArrowEvent(ScreenDirection.RIGHT);
}
if (event != null) {
ComponentPath path = screen.nextFocusPath(event);
if (path != null) {
accessor.invokeChangeFocus(path);
lastMoved = 0;
var newFocusTree = getFocusTree();
while (!newFocusTree.isEmpty() && !focuses.contains(newFocusTree.peek())) {
ComponentProcessorProvider.provide(newFocusTree.poll()).onFocusGained(this, controller);
}
}
}
}
protected void handleButtons(Controller controller) {
var focusTree = getFocusTree();
while (!focusTree.isEmpty()) {
var focused = focusTree.poll();
var processor = ComponentProcessorProvider.provide(focused);
if (processor.overrideControllerButtons(this, controller)) return;
}
if (controller.bindings().GUI_PRESS.justPressed())
screen.keyPressed(GLFW.GLFW_KEY_ENTER, 0, 0);
if (controller.bindings().GUI_BACK.justPressed())
screen.onClose();
}
protected void handleVMouseNavigation(Controller controller) {
}
public void onWidgetRebuild() {
setInitialFocus();
}
public void onVirtualMouseToggled(boolean enabled) {
if (enabled) {
((ScreenAccessor) screen).invokeClearFocus();
} else {
setInitialFocus();
}
}
protected void setInitialFocus() {
if (screen.getFocused() == null && Controlify.instance().currentInputMode() == InputMode.CONTROLLER && !Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) {
var accessor = (ScreenAccessor) screen;
ComponentPath path = screen.nextFocusPath(accessor.invokeCreateArrowEvent(ScreenDirection.DOWN));
if (path != null) {
accessor.invokeChangeFocus(path);
lastMoved = 0;
}
}
}
public boolean forceVirtualMouse() {
return false;
}
protected Queue<GuiEventListener> getFocusTree() {
if (screen.getFocused() == null) return new ArrayDeque<>();
var tree = new ArrayDeque<GuiEventListener>();
var focused = screen.getFocused();
tree.add(focused);
while (focused instanceof CustomFocus customFocus) {
focused = customFocus.getCustomFocus();
if (focused != null)
tree.addFirst(focused);
}
return tree;
}
}

View File

@ -0,0 +1,11 @@
package dev.isxander.controlify.screenop;
import net.minecraft.client.gui.screens.Screen;
public interface ScreenProcessorProvider {
ScreenProcessor<?> screenProcessor();
static ScreenProcessor<?> provide(Screen screen) {
return ((ScreenProcessorProvider) screen).screenProcessor();
}
}

View File

@ -0,0 +1,26 @@
package dev.isxander.controlify.screenop.compat.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.component.ComponentProcessor;
import dev.isxander.controlify.controller.Controller;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.AbstractButton;
public class AbstractButtonComponentProcessor implements ComponentProcessor {
private final AbstractButton button;
public AbstractButtonComponentProcessor(AbstractButton button) {
this.button = button;
}
@Override
public boolean overrideControllerButtons(ScreenProcessor<?> screen, Controller controller) {
if (controller.bindings().GUI_PRESS.justPressed()) {
button.playDownSound(Minecraft.getInstance().getSoundManager());
button.onPress();
return true;
}
return false;
}
}

View File

@ -0,0 +1,31 @@
package dev.isxander.controlify.screenop.compat.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.mixins.compat.screenop.vanilla.CreativeModeInventoryScreenAccessor;
import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen;
import net.minecraft.world.item.CreativeModeTabs;
public class CreativeModeInventoryScreenProcessor extends ScreenProcessor<CreativeModeInventoryScreen> {
public CreativeModeInventoryScreenProcessor(CreativeModeInventoryScreen screen) {
super(screen);
}
@Override
protected void handleVMouseNavigation(Controller controller) {
var accessor = (CreativeModeInventoryScreenAccessor) screen;
if (controller.bindings().GUI_NEXT_TAB.justPressed()) {
var tabs = CreativeModeTabs.tabs();
int newIndex = tabs.indexOf(accessor.getSelectedTab()) + 1;
if (newIndex >= tabs.size()) newIndex = 0;
accessor.invokeSelectTab(tabs.get(newIndex));
}
if (controller.bindings().GUI_PREV_TAB.justPressed()) {
var tabs = CreativeModeTabs.tabs();
int newIndex = tabs.indexOf(accessor.getSelectedTab()) - 1;
if (newIndex < 0) newIndex = tabs.size() - 1;
accessor.invokeSelectTab(tabs.get(newIndex));
}
}
}

View File

@ -0,0 +1,25 @@
package dev.isxander.controlify.screenop.compat.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.controller.Controller;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen;
import net.minecraft.client.gui.screens.multiplayer.ServerSelectionList;
public class JoinMultiplayerScreenProcessor extends ScreenProcessor<JoinMultiplayerScreen> {
private final ServerSelectionList list;
public JoinMultiplayerScreenProcessor(JoinMultiplayerScreen screen, ServerSelectionList list) {
super(screen);
this.list = list;
}
@Override
protected void handleButtons(Controller controller) {
if (screen.getFocused() instanceof Button && controller.bindings().GUI_BACK.justPressed()) {
screen.setFocused(list);
}
super.handleButtons(controller);
}
}

View File

@ -0,0 +1,35 @@
package dev.isxander.controlify.screenop.compat.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.component.ComponentProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.mixins.compat.screenop.vanilla.OptionsSubScreenAccessor;
import net.minecraft.client.Minecraft;
public class LanguageSelectionListComponentProcessor implements ComponentProcessor {
private final String code;
public LanguageSelectionListComponentProcessor(String code) {
this.code = code;
}
@Override
public boolean overrideControllerButtons(ScreenProcessor<?> screen, Controller controller) {
if (controller.bindings().GUI_PRESS.justPressed()) {
var minecraft = Minecraft.getInstance();
var languageManager = minecraft.getLanguageManager();
if (!code.equals(languageManager.getSelected())) {
languageManager.setSelected(code);
minecraft.options.languageCode = code;
minecraft.reloadResourcePacks();
minecraft.options.save();
}
minecraft.setScreen(((OptionsSubScreenAccessor) screen.screen).getLastScreen());
return true;
}
return false;
}
}

View File

@ -0,0 +1,25 @@
package dev.isxander.controlify.screenop.compat.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.mixins.compat.screenop.vanilla.SelectWorldScreenAccessor;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen;
public class SelectWorldScreenProcessor extends ScreenProcessor<SelectWorldScreen> {
public SelectWorldScreenProcessor(SelectWorldScreen screen) {
super(screen);
}
@Override
protected void handleButtons(Controller controller) {
if (screen.getFocused() != null && screen.getFocused() instanceof Button) {
if (controller.bindings().GUI_BACK.justPressed()) {
screen.setFocused(((SelectWorldScreenAccessor) screen).getList());
return;
}
}
super.handleButtons(controller);
}
}

View File

@ -0,0 +1,18 @@
package dev.isxander.controlify.screenop.compat.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.component.ComponentProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.mixins.compat.screenop.vanilla.JoinMultiplayerScreenAccessor;
public class ServerSelectionListEntryComponentProcessor implements ComponentProcessor {
@Override
public boolean overrideControllerButtons(ScreenProcessor<?> screen, Controller controller) {
if (controller.bindings().GUI_PRESS.justPressed()) {
screen.screen.setFocused(((JoinMultiplayerScreenAccessor) screen.screen).getSelectButton());
return true;
}
return true;
}
}

View File

@ -0,0 +1,71 @@
package dev.isxander.controlify.screenop.compat.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.component.ComponentProcessor;
import dev.isxander.controlify.controller.Controller;
import net.minecraft.client.gui.components.AbstractSliderButton;
import org.lwjgl.glfw.GLFW;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class SliderComponentProcessor implements ComponentProcessor {
private final Supplier<Boolean> canChangeValueGetter;
private final Consumer<Boolean> canChangeValueSetter;
private final AbstractSliderButton component;
private static final int SLIDER_CHANGE_DELAY = 1;
private int lastSliderChange = SLIDER_CHANGE_DELAY;
public SliderComponentProcessor(AbstractSliderButton component, Supplier<Boolean> canChangeValueGetter, Consumer<Boolean> canChangeValueSetter) {
this.component = component;
this.canChangeValueGetter = canChangeValueGetter;
this.canChangeValueSetter = canChangeValueSetter;
}
@Override
public boolean overrideControllerNavigation(ScreenProcessor<?> screen, Controller controller) {
if (!this.canChangeValueGetter.get()) return false;
var canSliderChange = ++lastSliderChange > SLIDER_CHANGE_DELAY;
var axes = controller.state().axes();
var buttons = controller.state().buttons();
if (axes.leftStickX() > 0.5f || buttons.dpadRight()) {
if (canSliderChange) {
component.keyPressed(GLFW.GLFW_KEY_RIGHT, 0, 0);
lastSliderChange = 0;
}
return true;
} else if (axes.leftStickX() < -0.5f || buttons.dpadLeft()) {
if (canSliderChange) {
component.keyPressed(GLFW.GLFW_KEY_LEFT, 0, 0);
lastSliderChange = 0;
}
return true;
}
return false;
}
@Override
public boolean overrideControllerButtons(ScreenProcessor<?> screen, Controller controller) {
if (!this.canChangeValueGetter.get()) return false;
if (controller.bindings().GUI_BACK.justPressed()) {
this.canChangeValueSetter.accept(false);
return true;
}
return false;
}
@Override
public void onFocusGained(ScreenProcessor<?> screen, Controller controller) {
System.out.println("navigated!");
this.canChangeValueSetter.accept(false);
}
}

View File

@ -0,0 +1,21 @@
package dev.isxander.controlify.screenop.compat.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.component.ComponentProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.mixins.compat.screenop.vanilla.SelectWorldScreenAccessor;
import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen;
public class WorldListEntryComponentProcessor implements ComponentProcessor {
@Override
public boolean overrideControllerButtons(ScreenProcessor screen, Controller controller) {
if (controller.bindings().GUI_PRESS.justPressed()) {
var selectWorldScreen = (SelectWorldScreen) screen.screen;
selectWorldScreen.setFocused(((SelectWorldScreenAccessor) selectWorldScreen).getSelectButton());
return true;
}
return false;
}
}

View File

@ -0,0 +1,33 @@
package dev.isxander.controlify.screenop.compat.yacl;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.component.ComponentProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.yacl.gui.controllers.cycling.CyclingControllerElement;
public class CyclingControllerElementComponentProcessor implements ComponentProcessor {
private final CyclingControllerElement cyclingController;
private int lastInput = 0;
public CyclingControllerElementComponentProcessor(CyclingControllerElement cyclingController) {
this.cyclingController = cyclingController;
}
@Override
public boolean overrideControllerNavigation(ScreenProcessor<?> screen, Controller controller) {
var rightStickX = controller.state().axes().rightStickX();
var rightStickY = controller.state().axes().rightStickY();
var input = Math.abs(rightStickX) > Math.abs(rightStickY) ? rightStickX : rightStickY;
var inputI = Math.abs(input) < controller.config().buttonActivationThreshold ? 0 : input > 0 ? 1 : -1;
if (inputI != 0 && inputI != lastInput) {
cyclingController.cycleValue(input > 0 ? 1 : -1);
lastInput = inputI;
return true;
}
lastInput = inputI;
return false;
}
}

View File

@ -0,0 +1,37 @@
package dev.isxander.controlify.screenop.compat.yacl;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.component.ComponentProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.yacl.gui.controllers.slider.SliderControllerElement;
public class SliderControllerElementComponentProcessor implements ComponentProcessor {
private final SliderControllerElement slider;
private int ticksSinceIncrement = 0;
private int lastInput = 0;
public SliderControllerElementComponentProcessor(SliderControllerElement element) {
this.slider = element;
}
@Override
public boolean overrideControllerButtons(ScreenProcessor<?> screen, Controller controller) {
ticksSinceIncrement++;
var rightStickX = controller.state().axes().rightStickX();
var rightStickY = controller.state().axes().rightStickY();
var input = Math.abs(rightStickX) > Math.abs(rightStickY) ? rightStickX : rightStickY;
if (Math.abs(input) > controller.config().buttonActivationThreshold) {
if (ticksSinceIncrement > controller.config().screenRepeatNavigationDelay || input != lastInput) {
slider.incrementValue(lastInput = input > 0 ? 1 : -1);
ticksSinceIncrement = 0;
return true;
}
} else {
this.lastInput = 0;
}
return false;
}
}

View File

@ -0,0 +1,32 @@
package dev.isxander.controlify.screenop.compat.yacl;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.yacl.gui.YACLScreen;
public class YACLScreenProcessor extends ScreenProcessor<YACLScreen> {
public YACLScreenProcessor(YACLScreen screen) {
super(screen);
}
@Override
protected void handleComponentNavigation(Controller controller) {
if (controller.bindings().GUI_NEXT_TAB.justPressed()) {
var idx = screen.getCurrentCategoryIdx() + 1;
if (idx >= screen.config.categories().size()) idx = 0;
screen.changeCategory(idx);
}
if (controller.bindings().GUI_PREV_TAB.justPressed()) {
var idx = screen.getCurrentCategoryIdx() - 1;
if (idx < 0) idx = screen.config.categories().size() - 1;
screen.changeCategory(idx);
}
super.handleComponentNavigation(controller);
}
@Override
protected void setInitialFocus() {
screen.setFocused(screen.optionList);
}
}

View File

@ -0,0 +1,19 @@
package dev.isxander.controlify.screenop.component;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.controller.Controller;
public interface ComponentProcessor {
ComponentProcessor EMPTY = new ComponentProcessor(){};
default boolean overrideControllerNavigation(ScreenProcessor<?> screen, Controller controller) {
return false;
}
default boolean overrideControllerButtons(ScreenProcessor<?> screen, Controller controller) {
return false;
}
default void onFocusGained(ScreenProcessor<?> screen, Controller controller) {
}
}

View File

@ -0,0 +1,13 @@
package dev.isxander.controlify.screenop.component;
import net.minecraft.client.gui.components.events.GuiEventListener;
public interface ComponentProcessorProvider {
ComponentProcessor componentProcessor();
static ComponentProcessor provide(GuiEventListener component) {
if (component instanceof ComponentProcessorProvider provider)
return provider.componentProcessor();
return ComponentProcessor.EMPTY;
}
}

View File

@ -0,0 +1,7 @@
package dev.isxander.controlify.screenop.component;
import net.minecraft.client.gui.components.events.GuiEventListener;
public interface CustomFocus {
GuiEventListener getCustomFocus();
}