forked from Clones/Controlify
➕ Controller conflict detection
This commit is contained in:
@ -1,12 +1,15 @@
|
||||
package dev.isxander.controlify.api.bind;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import dev.isxander.controlify.bindings.BindContext;
|
||||
import dev.isxander.controlify.bindings.IBind;
|
||||
import dev.isxander.yacl.api.Option;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
public interface ControllerBinding {
|
||||
@ -46,17 +49,20 @@ public interface ControllerBinding {
|
||||
|
||||
ResourceLocation id();
|
||||
|
||||
Set<BindContext> contexts();
|
||||
|
||||
/**
|
||||
* The vanilla override of the binding. Null if there is no override.
|
||||
*/
|
||||
@Nullable KeyMappingOverride override();
|
||||
|
||||
IBind<?> getBind();
|
||||
void resetBind();
|
||||
boolean isUnbound();
|
||||
|
||||
BindRenderer renderer();
|
||||
|
||||
Option<?> generateYACLOption();
|
||||
Option.Builder<?> startYACLOption();
|
||||
|
||||
JsonObject toJson();
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package dev.isxander.controlify.api.bind;
|
||||
|
||||
import dev.isxander.controlify.bindings.BindContext;
|
||||
import dev.isxander.controlify.bindings.ControllerBindingImpl;
|
||||
import dev.isxander.controlify.bindings.GamepadBinds;
|
||||
import dev.isxander.controlify.bindings.IBind;
|
||||
@ -73,6 +74,8 @@ public interface ControllerBindingBuilder<T extends ControllerState> {
|
||||
*/
|
||||
ControllerBindingBuilder<T> category(Component category);
|
||||
|
||||
ControllerBindingBuilder<T> context(BindContext... contexts);
|
||||
|
||||
/**
|
||||
* Specifies are vanilla override for the binding.
|
||||
* Will emulate presses of the vanilla keybind when the controller binding is pressed.
|
||||
|
@ -0,0 +1,64 @@
|
||||
package dev.isxander.controlify.bindings;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class BindContext {
|
||||
private final ResourceLocation context;
|
||||
private final Set<BindContext> parents;
|
||||
private final Set<ResourceLocation> flattened;
|
||||
|
||||
public BindContext(ResourceLocation context, Set<BindContext> parents) {
|
||||
this.context = context;
|
||||
this.parents = parents;
|
||||
|
||||
Set<ResourceLocation> flattened = new HashSet<>();
|
||||
flattened.add(context());
|
||||
parents().forEach(p -> flattened.addAll(p.flattened()));
|
||||
this.flattened = ImmutableSet.copyOf(flattened);
|
||||
}
|
||||
|
||||
public ResourceLocation context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public Set<BindContext> parents() {
|
||||
return parents;
|
||||
}
|
||||
|
||||
public Set<ResourceLocation> flattened() {
|
||||
return flattened;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||
var that = (BindContext) obj;
|
||||
return Objects.equals(this.context, that.context) &&
|
||||
Objects.equals(this.parents, that.parents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(context, parents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BindContext[" +
|
||||
"context=" + context + ", " +
|
||||
"parents=" + parents + ']';
|
||||
}
|
||||
|
||||
public static Set<ResourceLocation> flatten(Set<BindContext> contexts) {
|
||||
return contexts.stream()
|
||||
.flatMap(ctx -> ctx.flattened.stream())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package dev.isxander.controlify.bindings;
|
||||
|
||||
import dev.isxander.controlify.Controlify;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public final class BindContexts {
|
||||
public static final BindContext
|
||||
INGAME = ctx("ingame"),
|
||||
GUI = ctx("gui"),
|
||||
GUI_VMOUSE = ctx("gui_vmouse"),
|
||||
CONTROLIFY_CONFIG = ctx("controlify_config", GUI),
|
||||
INVENTORY = ctx("inventory", GUI_VMOUSE);
|
||||
|
||||
private static BindContext ctx(String path, BindContext... parents) {
|
||||
return new BindContext(Controlify.id(path), Set.of(parents));
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package dev.isxander.controlify.bindings;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dev.isxander.controlify.Controlify;
|
||||
@ -36,11 +37,12 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
||||
private BindRenderer renderer;
|
||||
private final ResourceLocation id;
|
||||
private final Component name, description, category;
|
||||
private final Set<BindContext> contexts;
|
||||
private final KeyMappingOverride override;
|
||||
|
||||
private static final Map<Controller<?, ?>, Set<IBind<?>>> pressedBinds = new HashMap<>();
|
||||
|
||||
private ControllerBindingImpl(Controller<T, ?> controller, IBind<T> defaultBind, ResourceLocation id, KeyMappingOverride vanillaOverride, Component name, Component description, Component category) {
|
||||
private ControllerBindingImpl(Controller<T, ?> controller, IBind<T> defaultBind, ResourceLocation id, KeyMappingOverride vanillaOverride, Component name, Component description, Component category, Set<BindContext> contexts) {
|
||||
this.controller = controller;
|
||||
this.bind = this.defaultBind = defaultBind;
|
||||
this.renderer = new BindRendererImpl(bind);
|
||||
@ -49,6 +51,7 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.category = category;
|
||||
this.contexts = ImmutableSet.copyOf(contexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -133,6 +136,16 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
||||
return category;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<BindContext> contexts() {
|
||||
return contexts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBind<T> getBind() {
|
||||
return bind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnbound() {
|
||||
return bind instanceof EmptyBind;
|
||||
@ -154,7 +167,7 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
||||
}
|
||||
|
||||
@Override
|
||||
public Option<?> generateYACLOption() {
|
||||
public Option.Builder<?> startYACLOption() {
|
||||
Option.Builder<IBind<T>> option = Option.createBuilder((Class<IBind<T>>) (Class<?>) IBind.class)
|
||||
.name(name())
|
||||
.binding(defaultBind(), this::currentBind, this::setCurrentBind)
|
||||
@ -166,7 +179,7 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
||||
((Option.Builder<IBind<JoystickState>>) (Object) option).controller(opt -> new JoystickBindController(opt, joystick));
|
||||
}
|
||||
|
||||
return option.build();
|
||||
return option;
|
||||
}
|
||||
|
||||
// FIXME: very hack solution please remove me
|
||||
@ -197,6 +210,7 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
||||
private ResourceLocation id;
|
||||
private Component name = null, description = null, category = null;
|
||||
private KeyMappingOverride override = null;
|
||||
private final Set<BindContext> contexts = new HashSet<>();
|
||||
|
||||
public ControllerBindingBuilderImpl(Controller<T, ?> controller) {
|
||||
this.controller = controller;
|
||||
@ -248,6 +262,12 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerBindingBuilder<T> context(BindContext... contexts) {
|
||||
this.contexts.addAll(Set.of(contexts));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerBindingBuilder<T> vanillaOverride(KeyMapping keyMapping, BooleanSupplier toggleable) {
|
||||
this.override = new KeyMappingOverride(keyMapping, toggleable);
|
||||
@ -276,7 +296,7 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
||||
}
|
||||
}
|
||||
|
||||
return new ControllerBindingImpl<>(controller, bind, id, override, name, description, category);
|
||||
return new ControllerBindingImpl<>(controller, bind, id, override, name, description, category, contexts);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,47 +78,56 @@ public class ControllerBindings<T extends ControllerState> {
|
||||
.identifier("controlify", "walk_forward")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_FORWARD)
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(WALK_BACKWARD = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "walk_backward")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_BACKWARD)
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(WALK_LEFT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "strafe_left")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_LEFT)
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(WALK_RIGHT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "strafe_right")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_RIGHT)
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(LOOK_UP = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "look_up")
|
||||
.defaultBind(GamepadBinds.RIGHT_STICK_FORWARD)
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(LOOK_DOWN = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "look_down")
|
||||
.defaultBind(GamepadBinds.RIGHT_STICK_BACKWARD)
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(LOOK_LEFT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "look_left")
|
||||
.defaultBind(GamepadBinds.RIGHT_STICK_LEFT)
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(LOOK_RIGHT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "look_right")
|
||||
.defaultBind(GamepadBinds.RIGHT_STICK_RIGHT)
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
if (controller instanceof GamepadController gamepad && gamepad.hasGyro()) {
|
||||
register(GAMEPAD_GYRO_BUTTON = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "gamepad_gyro_button")
|
||||
.defaultBind(new EmptyBind<>())
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
} else {
|
||||
GAMEPAD_GYRO_BUTTON = null;
|
||||
@ -127,209 +136,249 @@ public class ControllerBindings<T extends ControllerState> {
|
||||
.identifier("controlify", "jump")
|
||||
.defaultBind(GamepadBinds.A_BUTTON)
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(SPRINT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "sprint")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_PRESS)
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.vanillaOverride(options.keySprint, () -> controller.config().toggleSprint)
|
||||
.build());
|
||||
register(SNEAK = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "sneak")
|
||||
.defaultBind(GamepadBinds.RIGHT_STICK_PRESS)
|
||||
.category(MOVEMENT_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(ATTACK = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "attack")
|
||||
.defaultBind(GamepadBinds.RIGHT_TRIGGER)
|
||||
.category(GAMEPLAY_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.vanillaOverride(options.keyAttack, () -> false)
|
||||
.build());
|
||||
register(USE = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "use")
|
||||
.defaultBind(GamepadBinds.LEFT_TRIGGER)
|
||||
.category(GAMEPLAY_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.vanillaOverride(options.keyUse, () -> false)
|
||||
.build());
|
||||
register(DROP = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "drop")
|
||||
.defaultBind(GamepadBinds.DPAD_DOWN)
|
||||
.category(GAMEPLAY_CATEGORY)
|
||||
.context(BindContexts.INGAME, BindContexts.INVENTORY)
|
||||
.build());
|
||||
register(NEXT_SLOT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "next_slot")
|
||||
.defaultBind(GamepadBinds.RIGHT_BUMPER)
|
||||
.category(INVENTORY_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(PREV_SLOT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "prev_slot")
|
||||
.defaultBind(GamepadBinds.LEFT_BUMPER)
|
||||
.category(INVENTORY_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(PAUSE = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "pause")
|
||||
.defaultBind(GamepadBinds.START)
|
||||
.category(GAMEPLAY_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(INVENTORY = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "inventory")
|
||||
.defaultBind(GamepadBinds.Y_BUTTON)
|
||||
.category(INVENTORY_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.vanillaOverride(options.keyInventory, () -> false)
|
||||
.build());
|
||||
register(CHANGE_PERSPECTIVE = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "change_perspective")
|
||||
.defaultBind(GamepadBinds.BACK)
|
||||
.category(GAMEPLAY_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.vanillaOverride(options.keyTogglePerspective, () -> false)
|
||||
.build());
|
||||
register(SWAP_HANDS = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "swap_hands")
|
||||
.defaultBind(GamepadBinds.X_BUTTON)
|
||||
.category(INVENTORY_CATEGORY)
|
||||
.context(BindContexts.INGAME, BindContexts.INVENTORY)
|
||||
.vanillaOverride(options.keySwapOffhand, () -> false)
|
||||
.build());
|
||||
register(OPEN_CHAT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "open_chat")
|
||||
.defaultBind(GamepadBinds.DPAD_UP)
|
||||
.category(MISC_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.vanillaOverride(options.keyChat, () -> false)
|
||||
.build());
|
||||
register(GUI_PRESS = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "gui_press")
|
||||
.defaultBind(GamepadBinds.A_BUTTON)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI)
|
||||
.build());
|
||||
register(GUI_BACK = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "gui_back")
|
||||
.defaultBind(GamepadBinds.B_BUTTON)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI, BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(GUI_NEXT_TAB = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "gui_next_tab")
|
||||
.defaultBind(GamepadBinds.RIGHT_BUMPER)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI, BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(GUI_PREV_TAB = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "gui_prev_tab")
|
||||
.defaultBind(GamepadBinds.LEFT_BUMPER)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI, BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(GUI_ABSTRACT_ACTION_1 = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "gui_abstract_action_1")
|
||||
.defaultBind(GamepadBinds.X_BUTTON)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI)
|
||||
.build());
|
||||
register(GUI_ABSTRACT_ACTION_2 = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "gui_abstract_action_2")
|
||||
.defaultBind(GamepadBinds.Y_BUTTON)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI)
|
||||
.build());
|
||||
register(PICK_BLOCK = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "pick_block")
|
||||
.defaultBind(GamepadBinds.DPAD_LEFT)
|
||||
.category(GAMEPLAY_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.vanillaOverride(options.keyPickItem, () -> false)
|
||||
.build());
|
||||
register(TOGGLE_HUD_VISIBILITY = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "toggle_hud_visibility")
|
||||
.defaultBind(new EmptyBind<>())
|
||||
.category(MISC_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(SHOW_PLAYER_LIST = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "show_player_list")
|
||||
.defaultBind(GamepadBinds.DPAD_RIGHT)
|
||||
.category(MISC_CATEGORY)
|
||||
.context(BindContexts.INGAME)
|
||||
.build());
|
||||
register(VMOUSE_MOVE_UP = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "vmouse_move_up")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_FORWARD)
|
||||
.category(VMOUSE_CATEGORY)
|
||||
.context(BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(VMOUSE_MOVE_DOWN = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "vmouse_move_down")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_BACKWARD)
|
||||
.category(VMOUSE_CATEGORY)
|
||||
.context(BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(VMOUSE_MOVE_LEFT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "vmouse_move_left")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_LEFT)
|
||||
.category(VMOUSE_CATEGORY)
|
||||
.context(BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(VMOUSE_MOVE_RIGHT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "vmouse_move_right")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_RIGHT)
|
||||
.category(VMOUSE_CATEGORY)
|
||||
.context(BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(VMOUSE_LCLICK = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "vmouse_lclick")
|
||||
.defaultBind(GamepadBinds.A_BUTTON)
|
||||
.category(VMOUSE_CATEGORY)
|
||||
.context(BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(VMOUSE_RCLICK = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "vmouse_rclick")
|
||||
.defaultBind(GamepadBinds.X_BUTTON)
|
||||
.category(VMOUSE_CATEGORY)
|
||||
.context(BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(VMOUSE_SHIFT_CLICK = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "vmouse_shift_click")
|
||||
.defaultBind(GamepadBinds.Y_BUTTON)
|
||||
.category(VMOUSE_CATEGORY)
|
||||
.context(BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(VMOUSE_SCROLL_UP = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "vmouse_scroll_up")
|
||||
.defaultBind(GamepadBinds.RIGHT_STICK_FORWARD)
|
||||
.category(VMOUSE_CATEGORY)
|
||||
.context(BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(VMOUSE_SCROLL_DOWN = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "vmouse_scroll_down")
|
||||
.defaultBind(GamepadBinds.RIGHT_STICK_BACKWARD)
|
||||
.category(VMOUSE_CATEGORY)
|
||||
.context(BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(VMOUSE_SHIFT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "vmouse_shift")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_PRESS)
|
||||
.category(VMOUSE_CATEGORY)
|
||||
.context(BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(VMOUSE_TOGGLE = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "vmouse_toggle")
|
||||
.defaultBind(GamepadBinds.BACK)
|
||||
.category(VMOUSE_CATEGORY)
|
||||
.context(BindContexts.GUI_VMOUSE)
|
||||
.build());
|
||||
register(GUI_NAVI_UP = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "gui_navi_up")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_FORWARD)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI)
|
||||
.build());
|
||||
register(GUI_NAVI_DOWN = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "gui_navi_down")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_BACKWARD)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI)
|
||||
.build());
|
||||
register(GUI_NAVI_LEFT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "gui_navi_left")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_LEFT)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI)
|
||||
.build());
|
||||
register(GUI_NAVI_RIGHT = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "gui_navi_right")
|
||||
.defaultBind(GamepadBinds.LEFT_STICK_RIGHT)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI)
|
||||
.build());
|
||||
register(CYCLE_OPT_FORWARD = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "cycle_opt_forward")
|
||||
.defaultBind(GamepadBinds.RIGHT_STICK_RIGHT)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI)
|
||||
.build());
|
||||
register(CYCLE_OPT_BACKWARD = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "cycle_opt_backward")
|
||||
.defaultBind(GamepadBinds.RIGHT_STICK_LEFT)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.GUI)
|
||||
.build());
|
||||
register(CLEAR_BINDING = ControllerBindingBuilder.create(controller)
|
||||
.identifier("controlify", "clear_binding")
|
||||
.defaultBind(GamepadBinds.RIGHT_STICK_PRESS)
|
||||
.category(GUI_CATEGORY)
|
||||
.context(BindContexts.CONTROLIFY_CONFIG)
|
||||
.build());
|
||||
|
||||
for (var constructor : CUSTOM_BINDS.values()) {
|
||||
|
@ -13,11 +13,10 @@ public class YACLScreenProcessor extends ScreenProcessor<YACLScreen> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleComponentNavigation(Controller<?, ?> controller) {
|
||||
protected void handleButtons(Controller<?, ?> controller) {
|
||||
if (controller.bindings().GUI_ABSTRACT_ACTION_1.justPressed()) {
|
||||
this.playClackSound();
|
||||
screen.finishedSaveButton.onPress();
|
||||
return;
|
||||
}
|
||||
|
||||
if (controller.bindings().GUI_NEXT_TAB.justPressed()) {
|
||||
@ -31,12 +30,11 @@ public class YACLScreenProcessor extends ScreenProcessor<YACLScreen> {
|
||||
screen.changeCategory(idx);
|
||||
}
|
||||
|
||||
super.handleComponentNavigation(controller);
|
||||
super.handleButtons(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWidgetRebuild() {
|
||||
// currently doesn't work because TextScaledButtonWidget overrides renderString
|
||||
ButtonGuideApi.addGuideToButton(screen.finishedSaveButton, bindings -> bindings.GUI_ABSTRACT_ACTION_1, ButtonRenderPosition.TEXT, ButtonGuidePredicate.ALWAYS);
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ import dev.isxander.yacl.api.Option;
|
||||
import dev.isxander.yacl.api.utils.Dimension;
|
||||
import dev.isxander.yacl.gui.YACLScreen;
|
||||
import dev.isxander.yacl.gui.controllers.ControllerWidget;
|
||||
import dev.isxander.yacl.gui.utils.GuiUtils;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
@ -21,6 +22,7 @@ import java.util.Optional;
|
||||
public abstract class AbstractBindController<T extends ControllerState> implements Controller<IBind<T>> {
|
||||
private final Option<IBind<T>> option;
|
||||
public final dev.isxander.controlify.controller.Controller<T, ?> controller;
|
||||
private boolean conflicting;
|
||||
|
||||
public AbstractBindController(Option<IBind<T>> option, dev.isxander.controlify.controller.Controller<T, ?> controller) {
|
||||
this.option = option;
|
||||
@ -37,6 +39,14 @@ public abstract class AbstractBindController<T extends ControllerState> implemen
|
||||
return Component.empty();
|
||||
}
|
||||
|
||||
public void setConflicting(boolean conflicting) {
|
||||
this.conflicting = conflicting;
|
||||
}
|
||||
|
||||
public boolean getConflicting() {
|
||||
return this.conflicting;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract AbstractBindControllerElement<T> provideWidget(YACLScreen yaclScreen, Dimension<Integer> dimension);
|
||||
|
||||
@ -85,14 +95,16 @@ public abstract class AbstractBindController<T extends ControllerState> implemen
|
||||
public boolean overrideControllerButtons(ScreenProcessor<?> screen, dev.isxander.controlify.controller.Controller<?, ?> controller) {
|
||||
if (controller != control.controller) return true;
|
||||
|
||||
if (controller.bindings().CLEAR_BINDING.justPressed()) {
|
||||
control.option().requestSet(new EmptyBind<>());
|
||||
return true;
|
||||
}
|
||||
if (!justTookInput && !awaitingControllerInput) {
|
||||
if (controller.bindings().CLEAR_BINDING.justPressed()) {
|
||||
control.option().requestSet(new EmptyBind<>());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (controller.bindings().GUI_PRESS.justPressed() && !awaitingControllerInput) {
|
||||
ControllerBindHandler.setBindListener(this);
|
||||
return awaitingControllerInput = true;
|
||||
if (controller.bindings().GUI_PRESS.justPressed()) {
|
||||
ControllerBindHandler.setBindListener(this);
|
||||
return awaitingControllerInput = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (justTookInput) {
|
||||
@ -136,6 +148,11 @@ public abstract class AbstractBindController<T extends ControllerState> implemen
|
||||
return control.option().pendingValue().drawSize().width();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getValueColor() {
|
||||
return control.conflicting ? 0xFF5555 : super.getValueColor();
|
||||
}
|
||||
|
||||
public abstract Optional<IBind<T>> getPressedBind();
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,16 @@
|
||||
package dev.isxander.controlify.config.gui;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import dev.isxander.controlify.Controlify;
|
||||
import dev.isxander.controlify.api.bind.ControllerBinding;
|
||||
import dev.isxander.controlify.bindings.ControllerBindingImpl;
|
||||
import dev.isxander.controlify.bindings.IBind;
|
||||
import dev.isxander.controlify.bindings.BindContext;
|
||||
import dev.isxander.controlify.config.GlobalSettings;
|
||||
import dev.isxander.controlify.controller.Controller;
|
||||
import dev.isxander.controlify.controller.ControllerConfig;
|
||||
import dev.isxander.controlify.controller.ControllerState;
|
||||
import dev.isxander.controlify.controller.gamepad.GamepadController;
|
||||
import dev.isxander.controlify.controller.gamepad.GamepadState;
|
||||
import dev.isxander.controlify.controller.gamepad.BuiltinGamepadTheme;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickController;
|
||||
import dev.isxander.controlify.controller.joystick.SingleJoystickController;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickState;
|
||||
import dev.isxander.controlify.controller.joystick.mapping.JoystickMapping;
|
||||
import dev.isxander.controlify.gui.screen.ControllerDeadzoneCalibrationScreen;
|
||||
import dev.isxander.controlify.reacharound.ReachAroundMode;
|
||||
@ -29,7 +24,6 @@ import dev.isxander.yacl.gui.controllers.TickBoxController;
|
||||
import dev.isxander.yacl.gui.controllers.cycling.CyclingListController;
|
||||
import dev.isxander.yacl.gui.controllers.cycling.EnumController;
|
||||
import dev.isxander.yacl.gui.controllers.slider.FloatSliderController;
|
||||
import dev.isxander.yacl.gui.controllers.slider.IntegerSliderController;
|
||||
import dev.isxander.yacl.gui.controllers.string.StringController;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.Util;
|
||||
@ -39,6 +33,7 @@ import net.minecraft.network.chat.CommonComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
@ -69,7 +64,7 @@ public class YACLHelper {
|
||||
.name(Component.translatable("controlify.gui.load_vibration_natives"))
|
||||
.tooltip(Component.translatable("controlify.gui.load_vibration_natives.tooltip"))
|
||||
.tooltip(Component.translatable("controlify.gui.load_vibration_natives.tooltip.warning").withStyle(ChatFormatting.RED))
|
||||
.binding(GlobalSettings.DEFAULT.loadVibrationNatives, () -> globalSettings.loadVibrationNatives, v -> globalSettings.loadVibrationNatives = v)
|
||||
.binding(true, () -> globalSettings.loadVibrationNatives, v -> globalSettings.loadVibrationNatives = v)
|
||||
.controller(opt -> new BooleanController(opt, BooleanController.YES_NO_FORMATTER, false))
|
||||
.flag(OptionFlag.GAME_RESTART)
|
||||
.build())
|
||||
@ -114,7 +109,7 @@ public class YACLHelper {
|
||||
return yacl.build().generateScreen(parent);
|
||||
}
|
||||
|
||||
private static ConfigCategory createControllerCategory(Controller<?, ?> controller, Option<Boolean> globalVibrationOption) {
|
||||
private static <S extends ControllerState, C extends ControllerConfig> ConfigCategory createControllerCategory(Controller<S, C> controller, Option<Boolean> globalVibrationOption) {
|
||||
if (!controller.canBeUsed()) {
|
||||
return PlaceholderCategory.createBuilder()
|
||||
.name(Component.literal(controller.name()))
|
||||
@ -245,16 +240,45 @@ public class YACLHelper {
|
||||
|
||||
var controlsGroup = OptionGroup.createBuilder()
|
||||
.name(Component.translatable("controlify.gui.group.controls"));
|
||||
List<OptionBindPair> optionBinds = new ArrayList<>();
|
||||
groupBindings(controller.bindings().registry().values()).forEach((categoryName, bindGroup) -> {
|
||||
controlsGroup.option(LabelOption.create(categoryName));
|
||||
controlsGroup.options(bindGroup.stream().map(ControllerBinding::generateYACLOption).toList());
|
||||
controlsGroup.options(bindGroup.stream().map(binding -> {
|
||||
Option.Builder<?> option = binding.startYACLOption()
|
||||
.listener((opt, val) -> updateConflictingBinds(optionBinds));
|
||||
|
||||
Option<?> built = option.build();
|
||||
optionBinds.add(new OptionBindPair(built, binding));
|
||||
return built;
|
||||
}).toList());
|
||||
});
|
||||
updateConflictingBinds(optionBinds);
|
||||
|
||||
category.group(controlsGroup.build());
|
||||
|
||||
return category.build();
|
||||
}
|
||||
|
||||
private static void updateConflictingBinds(List<OptionBindPair> all) {
|
||||
all.forEach(pair -> ((AbstractBindController<?>) pair.option().controller()).setConflicting(false));
|
||||
|
||||
for (OptionBindPair opt : all) {
|
||||
var ctxs = BindContext.flatten(opt.binding().contexts());
|
||||
|
||||
List<OptionBindPair> conflicting = all.stream()
|
||||
.filter(pair -> pair.binding() != opt.binding())
|
||||
.filter(pair -> {
|
||||
boolean contextsMatch = BindContext.flatten(pair.binding().contexts())
|
||||
.stream()
|
||||
.anyMatch(ctxs::contains);
|
||||
boolean bindMatches = pair.option().pendingValue().equals(opt.option().pendingValue());
|
||||
return contextsMatch && bindMatches;
|
||||
}).toList();
|
||||
|
||||
conflicting.forEach(conflict -> ((AbstractBindController<?>) conflict.option().controller()).setConflicting(true));
|
||||
}
|
||||
}
|
||||
|
||||
private static OptionGroup makeVibrationGroup(Option<Boolean> globalVibrationOption, Controller<?, ?> controller) {
|
||||
boolean canRumble = controller.supportsRumble();
|
||||
var config = controller.config();
|
||||
@ -428,8 +452,11 @@ public class YACLHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends ControllerState> Map<Component, List<ControllerBinding>> groupBindings(Collection<ControllerBinding> bindings) {
|
||||
private static Map<Component, List<ControllerBinding>> groupBindings(Collection<ControllerBinding> bindings) {
|
||||
return bindings.stream()
|
||||
.collect(Collectors.groupingBy(ControllerBinding::category, LinkedHashMap::new, Collectors.toList()));
|
||||
}
|
||||
|
||||
private record OptionBindPair(Option<?> option, ControllerBinding binding) {
|
||||
}
|
||||
}
|
||||
|
@ -34,8 +34,11 @@ public class ScreenProcessor<T extends Screen> {
|
||||
|
||||
public void onControllerUpdate(Controller<?, ?> controller) {
|
||||
if (!Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) {
|
||||
handleComponentNavigation(controller);
|
||||
handleButtons(controller);
|
||||
if (!handleComponentNavOverride(controller))
|
||||
handleComponentNavigation(controller);
|
||||
|
||||
if (!handleComponentButtonOverride(controller))
|
||||
handleButtons(controller);
|
||||
} else {
|
||||
handleScreenVMouse(controller);
|
||||
}
|
||||
@ -58,13 +61,7 @@ public class ScreenProcessor<T extends Screen> {
|
||||
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 focuses = List.copyOf(getFocusTree());
|
||||
|
||||
var accessor = (ScreenAccessor) screen;
|
||||
|
||||
@ -114,15 +111,9 @@ public class ScreenProcessor<T extends Screen> {
|
||||
}
|
||||
|
||||
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())
|
||||
if (controller.bindings().GUI_PRESS.justPressed()) {
|
||||
screen.keyPressed(GLFW.GLFW_KEY_ENTER, 0, 0);
|
||||
}
|
||||
if (controller.bindings().GUI_BACK.justPressed()) {
|
||||
this.playClackSound();
|
||||
screen.onClose();
|
||||
@ -133,6 +124,27 @@ public class ScreenProcessor<T extends Screen> {
|
||||
|
||||
}
|
||||
|
||||
protected boolean handleComponentButtonOverride(Controller<?, ?> controller) {
|
||||
var focusTree = getFocusTree();
|
||||
while (!focusTree.isEmpty()) {
|
||||
var focused = focusTree.poll();
|
||||
var processor = ComponentProcessorProvider.provide(focused);
|
||||
if (processor.overrideControllerButtons(this, controller)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean handleComponentNavOverride(Controller<?, ?> controller) {
|
||||
var focusTree = getFocusTree();
|
||||
while (!focusTree.isEmpty()) {
|
||||
var focused = focusTree.poll();
|
||||
var processor = ComponentProcessorProvider.provide(focused);
|
||||
if (processor.overrideControllerNavigation(this, controller)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void handleTabNavigation(Controller<?, ?> controller) {
|
||||
var nextTab = controller.bindings().GUI_NEXT_TAB.justPressed();
|
||||
var prevTab = controller.bindings().GUI_PREV_TAB.justPressed();
|
||||
|
Reference in New Issue
Block a user