1
0
forked from Clones/Controlify

vmouse snapping

This commit is contained in:
isXander
2023-02-10 21:00:39 +00:00
parent 773aebe04e
commit 9e0440a7f4
41 changed files with 274 additions and 80 deletions

View File

@ -0,0 +1,19 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.screenop.ComponentProcessor;
import dev.isxander.controlify.screenop.ComponentProcessorProvider;
import dev.isxander.controlify.screenop.compat.vanilla.AbstractButtonComponentProcessor;
import net.minecraft.client.gui.components.AbstractButton;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(AbstractButton.class)
public class AbstractButtonMixin implements ComponentProcessorProvider {
@Unique private final AbstractButtonComponentProcessor controlify$processor
= new AbstractButtonComponentProcessor((AbstractButton) (Object) this);
@Override
public ComponentProcessor componentProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,18 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.screenop.CustomFocus;
import net.minecraft.client.gui.components.events.AbstractContainerEventHandler;
import net.minecraft.client.gui.components.events.GuiEventListener;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(AbstractContainerEventHandler.class)
public abstract class AbstractContainerEventHandlerMixin implements CustomFocus {
@Shadow public abstract @Nullable GuiEventListener getFocused();
@Override
public GuiEventListener getCustomFocus() {
return getFocused();
}
}

View File

@ -0,0 +1,16 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode;
import net.minecraft.client.gui.components.AbstractSelectionList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(AbstractSelectionList.class)
public class AbstractSelectionListMixin {
@ModifyExpressionValue(method = "setFocused", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/InputType;isKeyboard()Z"))
private boolean shouldEnsureEntryVisible(boolean keyboard) {
return keyboard || Controlify.instance().currentInputMode() == InputMode.CONTROLLER;
}
}

View File

@ -0,0 +1,41 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.screenop.ComponentProcessor;
import dev.isxander.controlify.screenop.ComponentProcessorProvider;
import dev.isxander.controlify.screenop.compat.vanilla.SliderComponentProcessor;
import net.minecraft.client.InputType;
import net.minecraft.client.gui.components.AbstractSliderButton;
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;
/**
* Mixin to insert a custom {@link ComponentProcessor} into slider to support left/right movement without navigating to next component.
*/
@Mixin(AbstractSliderButton.class)
public class AbstractSliderButtonMixin implements ComponentProcessorProvider {
@Shadow private boolean canChangeValue;
@Unique
private final SliderComponentProcessor controlify$processor = new SliderComponentProcessor(
(AbstractSliderButton) (Object) this,
() -> this.canChangeValue,
val -> this.canChangeValue = val
);
@ModifyExpressionValue(method = "setFocused", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getLastInputType()Lnet/minecraft/client/InputType;"))
private InputType shouldChangeValue(InputType type) {
if (Controlify.instance().currentInputMode() == InputMode.CONTROLLER)
return InputType.NONE; // none doesn't pass condition
return type;
}
@Override
public ComponentProcessor componentProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,18 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.screenop.CustomFocus;
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.events.GuiEventListener;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(ContainerObjectSelectionList.Entry.class)
public abstract class ContainerObjectSelectionListEntryMixin implements CustomFocus {
@Shadow public abstract @Nullable GuiEventListener getFocused();
@Override
public GuiEventListener getCustomFocus() {
return getFocused();
}
}

View File

@ -0,0 +1,16 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen;
import net.minecraft.world.item.CreativeModeTab;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(CreativeModeInventoryScreen.class)
public interface CreativeModeInventoryScreenAccessor {
@Accessor
CreativeModeTab getSelectedTab();
@Invoker
void invokeSelectTab(CreativeModeTab tab);
}

View File

@ -0,0 +1,19 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
import dev.isxander.controlify.screenop.compat.vanilla.CreativeModeInventoryScreenProcessor;
import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(CreativeModeInventoryScreen.class)
public class CreativeModeInventoryScreenMixin implements ScreenProcessorProvider {
@Unique private final CreativeModeInventoryScreenProcessor controlify$screenProcessor
= new CreativeModeInventoryScreenProcessor((CreativeModeInventoryScreen) (Object) this);
@Override
public ScreenProcessor<?> screenProcessor() {
return controlify$screenProcessor;
}
}

View File

@ -0,0 +1,12 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(JoinMultiplayerScreen.class)
public interface JoinMultiplayerScreenAccessor {
@Accessor
Button getSelectButton();
}

View File

@ -0,0 +1,23 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
import dev.isxander.controlify.screenop.compat.vanilla.JoinMultiplayerScreenProcessor;
import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen;
import net.minecraft.client.gui.screens.multiplayer.ServerSelectionList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@Mixin(JoinMultiplayerScreen.class)
public class JoinMultiplayerScreenMixin implements ScreenProcessorProvider {
@Shadow protected ServerSelectionList serverSelectionList;
@Unique private final JoinMultiplayerScreenProcessor controlify$processor
= new JoinMultiplayerScreenProcessor((JoinMultiplayerScreen) (Object) this, serverSelectionList);
@Override
public ScreenProcessor<?> screenProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,26 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.screenop.ComponentProcessor;
import dev.isxander.controlify.screenop.ComponentProcessorProvider;
import dev.isxander.controlify.screenop.compat.vanilla.LanguageSelectionListComponentProcessor;
import net.minecraft.client.gui.screens.LanguageSelectScreen;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@Mixin(LanguageSelectScreen.LanguageSelectionList.Entry.class)
public class LanguageSelectionListEntryMixin implements ComponentProcessorProvider {
@Shadow @Final String code;
@Unique private LanguageSelectionListComponentProcessor controlify$componentProcessor = null;
@Override
public ComponentProcessor componentProcessor() {
// lazily create the component processor so `code` is defined
if (controlify$componentProcessor == null)
controlify$componentProcessor = new LanguageSelectionListComponentProcessor(code);
return controlify$componentProcessor;
}
}

View File

@ -0,0 +1,12 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import net.minecraft.client.gui.screens.OptionsSubScreen;
import net.minecraft.client.gui.screens.Screen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(OptionsSubScreen.class)
public interface OptionsSubScreenAccessor {
@Accessor
Screen getLastScreen();
}

View File

@ -0,0 +1,20 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import net.minecraft.client.gui.ComponentPath;
import net.minecraft.client.gui.navigation.FocusNavigationEvent;
import net.minecraft.client.gui.navigation.ScreenDirection;
import net.minecraft.client.gui.screens.Screen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(Screen.class)
public interface ScreenAccessor {
@Invoker
FocusNavigationEvent.ArrowNavigation invokeCreateArrowEvent(ScreenDirection direction);
@Invoker
void invokeChangeFocus(ComponentPath path);
@Invoker
void invokeClearFocus();
}

View File

@ -0,0 +1,26 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
import dev.isxander.controlify.screenop.ScreenProcessor;
import net.minecraft.client.gui.screens.Screen;
import org.spongepowered.asm.mixin.Mixin;
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;
@Mixin(Screen.class)
public class ScreenMixin implements ScreenProcessorProvider {
@Unique
private final ScreenProcessor<Screen> controlify$processor = new ScreenProcessor<>((Screen) (Object) this);
@Override
public ScreenProcessor<Screen> screenProcessor() {
return controlify$processor;
}
@Inject(method = "rebuildWidgets", at = @At("RETURN"))
private void onScreenInit(CallbackInfo ci) {
screenProcessor().onWidgetRebuild();
}
}

View File

@ -0,0 +1,16 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen;
import net.minecraft.client.gui.screens.worldselection.WorldSelectionList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(SelectWorldScreen.class)
public interface SelectWorldScreenAccessor {
@Accessor
Button getSelectButton();
@Accessor
WorldSelectionList getList();
}

View File

@ -0,0 +1,17 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
import dev.isxander.controlify.screenop.compat.vanilla.SelectWorldScreenProcessor;
import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(SelectWorldScreen.class)
public class SelectWorldScreenMixin implements ScreenProcessorProvider {
private final SelectWorldScreenProcessor controlify$processor = new SelectWorldScreenProcessor((SelectWorldScreen) (Object) this);
@Override
public ScreenProcessor<?> screenProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,21 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.screenop.ComponentProcessor;
import dev.isxander.controlify.screenop.ComponentProcessorProvider;
import dev.isxander.controlify.screenop.compat.vanilla.ServerSelectionListEntryComponentProcessor;
import net.minecraft.client.gui.screens.multiplayer.ServerSelectionList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(ServerSelectionList.Entry.class)
public class ServerSelectionListEntryMixin implements ComponentProcessorProvider {
@Unique private final ServerSelectionListEntryComponentProcessor controlify$componentProcessor
= new ServerSelectionListEntryComponentProcessor();
@Override
public ComponentProcessor componentProcessor() {
return ((ServerSelectionList.Entry) (Object) this) instanceof ServerSelectionList.LANHeader
? ComponentProcessor.EMPTY
: controlify$componentProcessor;
}
}

View File

@ -0,0 +1,17 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.screenop.ComponentProcessor;
import dev.isxander.controlify.screenop.ComponentProcessorProvider;
import dev.isxander.controlify.screenop.compat.vanilla.WorldListEntryComponentProcessor;
import net.minecraft.client.gui.screens.worldselection.WorldSelectionList;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(WorldSelectionList.WorldListEntry.class)
public class WorldSelectionListEntryMixin implements ComponentProcessorProvider {
private final WorldListEntryComponentProcessor controlify$processor = new WorldListEntryComponentProcessor();
@Override
public ComponentProcessor componentProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,19 @@
package dev.isxander.controlify.mixins.feature.screenop.yacl;
import dev.isxander.controlify.screenop.ComponentProcessor;
import dev.isxander.controlify.screenop.ComponentProcessorProvider;
import dev.isxander.controlify.screenop.compat.yacl.CyclingControllerElementComponentProcessor;
import dev.isxander.yacl.gui.controllers.cycling.CyclingControllerElement;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(CyclingControllerElement.class)
public class CyclingControllerElementMixin implements ComponentProcessorProvider {
@Unique private final CyclingControllerElementComponentProcessor controlify$processor
= new CyclingControllerElementComponentProcessor((CyclingControllerElement) (Object) this);
@Override
public ComponentProcessor componentProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,19 @@
package dev.isxander.controlify.mixins.feature.screenop.yacl;
import dev.isxander.controlify.screenop.ComponentProcessor;
import dev.isxander.controlify.screenop.ComponentProcessorProvider;
import dev.isxander.controlify.screenop.compat.yacl.SliderControllerElementComponentProcessor;
import dev.isxander.yacl.gui.controllers.slider.SliderControllerElement;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(SliderControllerElement.class)
public class SliderControllerElementMixin implements ComponentProcessorProvider {
@Unique private final SliderControllerElementComponentProcessor controlify$processor
= new SliderControllerElementComponentProcessor((SliderControllerElement) (Object) this);
@Override
public ComponentProcessor componentProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,18 @@
package dev.isxander.controlify.mixins.feature.screenop.yacl;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
import dev.isxander.controlify.screenop.compat.yacl.YACLScreenProcessor;
import dev.isxander.yacl.gui.YACLScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(YACLScreen.class)
public class YACLScreenMixin implements ScreenProcessorProvider {
@Unique private final YACLScreenProcessor controlify$processor = new YACLScreenProcessor((YACLScreen) (Object) this);
@Override
public ScreenProcessor<?> screenProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,37 @@
package dev.isxander.controlify.mixins.feature.virtualmouse.snapping;
import dev.isxander.controlify.virtualmouse.ISnapBehaviour;
import dev.isxander.controlify.virtualmouse.SnapPoint;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.network.chat.Component;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.joml.Vector2i;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.Set;
import java.util.stream.Collectors;
@Mixin(AbstractContainerScreen.class)
public abstract class AbstractContainerScreenMixin<T extends AbstractContainerMenu> extends Screen implements ISnapBehaviour {
protected AbstractContainerScreenMixin(Component title) {
super(title);
}
@Shadow public abstract T getMenu();
@Shadow protected int leftPos;
@Shadow protected int topPos;
@Shadow protected int imageHeight;
@Override
public Set<SnapPoint> getSnapPoints() {
return getMenu().slots.stream()
.map(slot -> new SnapPoint(new Vector2i(leftPos + slot.x + 8, topPos + slot.y + 8), 24))
.collect(Collectors.toSet());
}
}

View File

@ -0,0 +1,48 @@
package dev.isxander.controlify.mixins.feature.virtualmouse.snapping;
import dev.isxander.controlify.virtualmouse.SnapPoint;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.CreativeModeTabs;
import org.joml.Vector2i;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.Set;
@Mixin(CreativeModeInventoryScreen.class)
public abstract class CreativeModeInventoryScreenMixin extends AbstractContainerScreenMixin<CreativeModeInventoryScreen.ItemPickerMenu> {
@Shadow protected abstract int getTabX(CreativeModeTab group);
@Shadow private float scrollOffs;
@Shadow private EditBox searchBox;
@Shadow protected abstract boolean canScroll();
protected CreativeModeInventoryScreenMixin(Component title) {
super(title);
}
@Override
public Set<SnapPoint> getSnapPoints() {
Set<SnapPoint> points = super.getSnapPoints();
for (var tab : CreativeModeTabs.tabs()) {
boolean topRow = tab.row() == CreativeModeTab.Row.TOP;
int x = leftPos + getTabX(tab);
int y = topPos + (topRow ? -28 : imageHeight - 4);
points.add(new SnapPoint(new Vector2i(x + 13, y + 16), 38));
}
if (canScroll()) {
int scrollTop = topPos + 18;
int scrollBottom = scrollTop + 112;
points.add(new SnapPoint(new Vector2i(leftPos + 175 + 6, scrollTop + (int)((float)(scrollBottom - scrollTop - 17) * scrollOffs) + 7), 15));
}
if (searchBox.isVisible())
points.add(new SnapPoint(new Vector2i(searchBox.getX() + searchBox.getWidth() / 2, searchBox.getY() + searchBox.getHeight() / 2), searchBox.getHeight() + 2));
return points;
}
}

View File

@ -0,0 +1,31 @@
package dev.isxander.controlify.mixins.feature.virtualmouse.snapping;
import dev.isxander.controlify.virtualmouse.ISnapBehaviour;
import dev.isxander.controlify.virtualmouse.SnapPoint;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import org.joml.Vector2i;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Mixin(Screen.class)
public abstract class ScreenMixin implements ISnapBehaviour {
@Shadow public abstract List<? extends GuiEventListener> children();
@Override
public Set<SnapPoint> getSnapPoints() {
return children().stream()
.filter(child -> child instanceof AbstractWidget)
.map(AbstractWidget.class::cast)
.map(widget -> new SnapPoint(
new Vector2i(widget.getX() + widget.getWidth() / 2, widget.getY() + widget.getHeight() / 2),
Math.min(widget.getWidth(), widget.getHeight()) + 5
))
.collect(Collectors.toSet());
}
}