1
0
forked from Clones/Controlify

refactor binding api (sorry enjarai)

This commit is contained in:
isXander
2023-04-14 20:11:57 +01:00
parent 07b57d5637
commit abe6b27cef
16 changed files with 183 additions and 109 deletions

View File

@ -0,0 +1,10 @@
package dev.isxander.controlify.api.bind;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.gui.DrawSize;
public interface BindRenderer {
DrawSize size();
void render(PoseStack poseStack, int x, int centerY);
}

View File

@ -1,8 +1,8 @@
package dev.isxander.controlify.bindings; package dev.isxander.controlify.api.bind;
import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.controller.Controller;
@FunctionalInterface @FunctionalInterface
public interface BindingSupplier { public interface BindingSupplier {
ControllerBinding<?> get(Controller<?, ?> controller); ControllerBinding onController(Controller<?, ?> controller);
} }

View File

@ -1,7 +1,6 @@
package dev.isxander.controlify.api.bind; package dev.isxander.controlify.api.bind;
import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.api.ControlifyApi;
import dev.isxander.controlify.bindings.BindingSupplier;
import dev.isxander.controlify.bindings.ControllerBindings; import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.bindings.GamepadBinds; import dev.isxander.controlify.bindings.GamepadBinds;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;

View File

@ -0,0 +1,37 @@
package dev.isxander.controlify.api.bind;
import com.google.gson.JsonObject;
import net.minecraft.client.KeyMapping;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import java.util.function.BooleanSupplier;
public interface ControllerBinding {
float state();
float prevState();
boolean held();
boolean prevHeld();
boolean justPressed();
boolean justReleased();
Component name();
Component description();
Component category();
ResourceLocation id();
KeyMappingOverride override();
void resetBind();
boolean isUnbound();
BindRenderer renderer();
JsonObject toJson();
record KeyMappingOverride(KeyMapping keyMapping, BooleanSupplier toggleable) {
}
}

View File

@ -1,6 +1,6 @@
package dev.isxander.controlify.api.bind; package dev.isxander.controlify.api.bind;
import dev.isxander.controlify.bindings.ControllerBinding; import dev.isxander.controlify.bindings.ControllerBindingImpl;
import dev.isxander.controlify.bindings.GamepadBinds; import dev.isxander.controlify.bindings.GamepadBinds;
import dev.isxander.controlify.bindings.IBind; import dev.isxander.controlify.bindings.IBind;
import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.controller.Controller;
@ -13,7 +13,7 @@ import java.util.function.BooleanSupplier;
public interface ControllerBindingBuilder<T extends ControllerState> { public interface ControllerBindingBuilder<T extends ControllerState> {
static <T extends ControllerState> ControllerBindingBuilder<T> create(Controller<T, ?> controller) { static <T extends ControllerState> ControllerBindingBuilder<T> create(Controller<T, ?> controller) {
return new ControllerBinding.ControllerBindingBuilderImpl<>(controller); return new ControllerBindingImpl.ControllerBindingBuilderImpl<>(controller);
} }
/** /**
@ -92,5 +92,5 @@ public interface ControllerBindingBuilder<T extends ControllerState> {
*/ */
ControllerBindingBuilder<T> vanillaOverride(KeyMapping keyMapping); ControllerBindingBuilder<T> vanillaOverride(KeyMapping keyMapping);
ControllerBinding<T> build(); ControllerBinding build();
} }

View File

@ -1,6 +1,6 @@
package dev.isxander.controlify.api.buttonguide; package dev.isxander.controlify.api.buttonguide;
import dev.isxander.controlify.bindings.ControllerBinding; import dev.isxander.controlify.api.bind.ControllerBinding;
import dev.isxander.controlify.bindings.ControllerBindings; import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.gui.ButtonGuideRenderer; import dev.isxander.controlify.gui.ButtonGuideRenderer;
import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.components.AbstractButton;
@ -25,7 +25,7 @@ public final class ButtonGuideApi {
*/ */
public static <T extends AbstractButton> void addGuideToButton( public static <T extends AbstractButton> void addGuideToButton(
T button, T button,
Function<ControllerBindings<?>, ControllerBinding<?>> binding, Function<ControllerBindings<?>, ControllerBinding> binding,
ButtonRenderPosition position, ButtonRenderPosition position,
ButtonGuidePredicate<T> renderPredicate) { ButtonGuidePredicate<T> renderPredicate) {
ButtonGuideRenderer.registerBindingForButton(button, binding, position, renderPredicate); ButtonGuideRenderer.registerBindingForButton(button, binding, position, renderPredicate);

View File

@ -1,6 +1,6 @@
package dev.isxander.controlify.api.ingameguide; package dev.isxander.controlify.api.ingameguide;
import dev.isxander.controlify.bindings.ControllerBinding; import dev.isxander.controlify.api.bind.ControllerBinding;
/** /**
* Allows you to register your own actions to the button guide. * Allows you to register your own actions to the button guide.
@ -16,7 +16,7 @@ public interface IngameGuideRegistry {
* @param priority the priority of the action, used to sort the list. * @param priority the priority of the action, used to sort the list.
* @param supplier the supplier for the name of the action. can be empty to hide the action. * @param supplier the supplier for the name of the action. can be empty to hide the action.
*/ */
void registerGuideAction(ControllerBinding<?> binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier supplier); void registerGuideAction(ControllerBinding binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier supplier);
/** /**
* Registers a new action to the button guide. * Registers a new action to the button guide.
@ -25,5 +25,5 @@ public interface IngameGuideRegistry {
* @param location the location of the action, left or right. * @param location the location of the action, left or right.
* @param supplier the supplier for the name of the action. can be empty to hide the action. * @param supplier the supplier for the name of the action. can be empty to hide the action.
*/ */
void registerGuideAction(ControllerBinding<?> binding, ActionLocation location, GuideActionNameSupplier supplier); void registerGuideAction(ControllerBinding binding, ActionLocation location, GuideActionNameSupplier supplier);
} }

View File

@ -1,9 +1,15 @@
package dev.isxander.controlify.bindings; package dev.isxander.controlify.bindings;
import com.google.gson.JsonObject;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.api.bind.BindRenderer;
import dev.isxander.controlify.api.bind.ControllerBinding;
import dev.isxander.controlify.api.bind.ControllerBindingBuilder; import dev.isxander.controlify.api.bind.ControllerBindingBuilder;
import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.ControllerState; import dev.isxander.controlify.controller.ControllerState;
import dev.isxander.controlify.controller.gamepad.GamepadController; import dev.isxander.controlify.controller.gamepad.GamepadController;
import dev.isxander.controlify.gui.DrawSize;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;
import net.minecraft.locale.Language; import net.minecraft.locale.Language;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
@ -17,19 +23,21 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
public class ControllerBinding<T extends ControllerState> { public class ControllerBindingImpl<T extends ControllerState> implements ControllerBinding {
private final Controller<T, ?> controller; private final Controller<T, ?> controller;
private IBind<T> bind; private IBind<T> bind;
private final IBind<T> defaultBind; private final IBind<T> defaultBind;
private BindRenderer renderer;
private final ResourceLocation id; private final ResourceLocation id;
private final Component name, description, category; private final Component name, description, category;
private final KeyMappingOverride override; private final KeyMappingOverride override;
private static final Map<Controller<?, ?>, Set<IBind<?>>> pressedBinds = new HashMap<>(); private static final Map<Controller<?, ?>, Set<IBind<?>>> pressedBinds = new HashMap<>();
private ControllerBinding(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) {
this.controller = controller; this.controller = controller;
this.bind = this.defaultBind = defaultBind; this.bind = this.defaultBind = defaultBind;
this.renderer = new BindRendererImpl(bind);
this.id = id; this.id = id;
this.override = vanillaOverride; this.override = vanillaOverride;
this.name = name; this.name = name;
@ -37,50 +45,27 @@ public class ControllerBinding<T extends ControllerState> {
this.category = category; this.category = category;
} }
@Deprecated @Override
public ControllerBinding(Controller<T, ?> controller, IBind<T> defaultBind, ResourceLocation id, KeyMapping override, BooleanSupplier toggleOverride) {
this.controller = controller;
this.bind = this.defaultBind = defaultBind;
this.id = id;
this.name = Component.translatable("controlify.binding." + id.getNamespace() + "." + id.getPath());
var descKey = "controlify.binding." + id.getNamespace() + "." + id.getPath() + ".desc";
this.description = Language.getInstance().has(descKey) ? Component.translatable(descKey) : Component.empty();
this.override = override != null ? new KeyMappingOverride(override, toggleOverride) : null;
this.category = null;
}
@Deprecated
public ControllerBinding(Controller<T, ?> controller, IBind<T> defaultBind, ResourceLocation id) {
this(controller, defaultBind, id, null, () -> false);
}
@Deprecated
@SuppressWarnings("unchecked")
public ControllerBinding(Controller<T, ?> controller, GamepadBinds defaultBind, ResourceLocation id, KeyMapping override, BooleanSupplier toggleOverride) {
this(controller, controller instanceof GamepadController gamepad ? (IBind<T>) defaultBind.forGamepad(gamepad) : new EmptyBind<>(), id, override, toggleOverride);
}
@Deprecated
public ControllerBinding(Controller<T, ?> controller, GamepadBinds defaultBind, ResourceLocation id) {
this(controller, defaultBind, id, null, () -> false);
}
public float state() { public float state() {
return bind.state(controller.state()); return bind.state(controller.state());
} }
@Override
public float prevState() { public float prevState() {
return bind.state(controller.prevState()); return bind.state(controller.prevState());
} }
@Override
public boolean held() { public boolean held() {
return bind.held(controller.state()); return bind.held(controller.state());
} }
@Override
public boolean prevHeld() { public boolean prevHeld() {
return bind.held(controller.prevState()); return bind.held(controller.prevState());
} }
@Override
public boolean justPressed() { public boolean justPressed() {
if (hasBindPressed(this)) return false; if (hasBindPressed(this)) return false;
@ -92,6 +77,7 @@ public class ControllerBinding<T extends ControllerState> {
} }
} }
@Override
public boolean justReleased() { public boolean justReleased() {
if (hasBindPressed(this)) return false; if (hasBindPressed(this)) return false;
@ -109,36 +95,58 @@ public class ControllerBinding<T extends ControllerState> {
public void setCurrentBind(IBind<T> bind) { public void setCurrentBind(IBind<T> bind) {
this.bind = bind; this.bind = bind;
this.renderer = new BindRendererImpl(bind);
Controlify.instance().config().setDirty();
} }
public IBind<T> defaultBind() { public IBind<T> defaultBind() {
return defaultBind; return defaultBind;
} }
@Override
public void resetBind() {
setCurrentBind(defaultBind());
}
public ResourceLocation id() { public ResourceLocation id() {
return id; return id;
} }
@Override
public Component name() { public Component name() {
return name; return name;
} }
@Override
public Component description() { public Component description() {
return description; return description;
} }
@Override
public Component category() { public Component category() {
return category; return category;
} }
public boolean unbound() { @Override
public boolean isUnbound() {
return bind instanceof EmptyBind; return bind instanceof EmptyBind;
} }
@Override
public KeyMappingOverride override() { public KeyMappingOverride override() {
return override; return override;
} }
@Override
public JsonObject toJson() {
return currentBind().toJson();
}
@Override
public BindRenderer renderer() {
return renderer;
}
// FIXME: very hack solution please remove me // FIXME: very hack solution please remove me
public static void clearPressedBinds(Controller<?, ?> controller) { public static void clearPressedBinds(Controller<?, ?> controller) {
@ -147,12 +155,12 @@ public class ControllerBinding<T extends ControllerState> {
} }
} }
private static boolean hasBindPressed(ControllerBinding<?> binding) { private static boolean hasBindPressed(ControllerBindingImpl<?> binding) {
var pressed = pressedBinds.getOrDefault(binding.controller, Set.of()); var pressed = pressedBinds.getOrDefault(binding.controller, Set.of());
return pressed.containsAll(getBinds(binding.bind)); return pressed.containsAll(getBinds(binding.bind));
} }
private static void addPressedBind(ControllerBinding<?> binding) { private static void addPressedBind(ControllerBindingImpl<?> binding) {
pressedBinds.computeIfAbsent(binding.controller, c -> new HashSet<>()).addAll(getBinds(binding.bind)); pressedBinds.computeIfAbsent(binding.controller, c -> new HashSet<>()).addAll(getBinds(binding.bind));
} }
@ -160,9 +168,6 @@ public class ControllerBinding<T extends ControllerState> {
return Set.of(bind); return Set.of(bind);
} }
public record KeyMappingOverride(KeyMapping keyMapping, BooleanSupplier toggleable) {
}
@ApiStatus.Internal @ApiStatus.Internal
public static final class ControllerBindingBuilderImpl<T extends ControllerState> implements ControllerBindingBuilder<T> { public static final class ControllerBindingBuilderImpl<T extends ControllerState> implements ControllerBindingBuilder<T> {
private final Controller<T, ?> controller; private final Controller<T, ?> controller;
@ -233,7 +238,7 @@ public class ControllerBinding<T extends ControllerState> {
} }
@Override @Override
public ControllerBinding<T> build() { public ControllerBinding build() {
Validate.notNull(id, "Identifier must be set"); Validate.notNull(id, "Identifier must be set");
Validate.notNull(bind, "Default bind must be set"); Validate.notNull(bind, "Default bind must be set");
Validate.notNull(category, "Category must be set"); Validate.notNull(category, "Category must be set");
@ -249,7 +254,19 @@ public class ControllerBinding<T extends ControllerState> {
} }
} }
return new ControllerBinding<>(controller, bind, id, override, name, description, category); return new ControllerBindingImpl<>(controller, bind, id, override, name, description, category);
}
}
private record BindRendererImpl(IBind<?> bind) implements BindRenderer {
@Override
public void render(PoseStack poseStack, int x, int centerY) {
bind.draw(poseStack, x, centerY);
}
@Override
public DrawSize size() {
return bind.drawSize();
} }
} }
} }

View File

@ -4,6 +4,7 @@ import com.google.gson.JsonObject;
import dev.isxander.controlify.Controlify; import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode; import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.api.bind.ControlifyBindingsApi; import dev.isxander.controlify.api.bind.ControlifyBindingsApi;
import dev.isxander.controlify.api.bind.ControllerBinding;
import dev.isxander.controlify.api.bind.ControllerBindingBuilder; import dev.isxander.controlify.api.bind.ControllerBindingBuilder;
import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.ControllerState; import dev.isxander.controlify.controller.ControllerState;
@ -25,7 +26,7 @@ import java.util.function.Function;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
public class ControllerBindings<T extends ControllerState> { public class ControllerBindings<T extends ControllerState> {
private static final Map<ResourceLocation, Function<ControllerBindings<?>, ControllerBinding<?>>> CUSTOM_BINDS = new LinkedHashMap<>(); private static final Map<ResourceLocation, Function<ControllerBindings<?>, ControllerBinding>> CUSTOM_BINDS = new LinkedHashMap<>();
private static final Set<KeyMapping> EXCLUDED_VANILLA_BINDS = new HashSet<>(); private static final Set<KeyMapping> EXCLUDED_VANILLA_BINDS = new HashSet<>();
public static final Component MOVEMENT_CATEGORY = Component.translatable("key.categories.movement"); public static final Component MOVEMENT_CATEGORY = Component.translatable("key.categories.movement");
@ -36,7 +37,7 @@ public class ControllerBindings<T extends ControllerState> {
public static final Component GUI_CATEGORY = Component.translatable("controlify.binding_category.gui"); public static final Component GUI_CATEGORY = Component.translatable("controlify.binding_category.gui");
public static final Component MISC_CATEGORY = Component.translatable("key.categories.misc"); public static final Component MISC_CATEGORY = Component.translatable("key.categories.misc");
public final ControllerBinding<T> public final ControllerBinding
WALK_FORWARD, WALK_BACKWARD, WALK_LEFT, WALK_RIGHT, WALK_FORWARD, WALK_BACKWARD, WALK_LEFT, WALK_RIGHT,
LOOK_UP, LOOK_DOWN, LOOK_LEFT, LOOK_RIGHT, LOOK_UP, LOOK_DOWN, LOOK_LEFT, LOOK_RIGHT,
GAMEPAD_GYRO_BUTTON, GAMEPAD_GYRO_BUTTON,
@ -64,7 +65,7 @@ public class ControllerBindings<T extends ControllerState> {
GUI_NAVI_UP, GUI_NAVI_DOWN, GUI_NAVI_LEFT, GUI_NAVI_RIGHT, GUI_NAVI_UP, GUI_NAVI_DOWN, GUI_NAVI_LEFT, GUI_NAVI_RIGHT,
CYCLE_OPT_FORWARD, CYCLE_OPT_BACKWARD; CYCLE_OPT_FORWARD, CYCLE_OPT_BACKWARD;
private final Map<ResourceLocation, ControllerBinding<T>> registry = new LinkedHashMap<>(); private final Map<ResourceLocation, ControllerBinding> registry = new LinkedHashMap<>();
private final Controller<T, ?> controller; private final Controller<T, ?> controller;
@ -333,7 +334,7 @@ public class ControllerBindings<T extends ControllerState> {
.build()); .build());
for (var constructor : CUSTOM_BINDS.values()) { for (var constructor : CUSTOM_BINDS.values()) {
register((ControllerBinding<T>) constructor.apply(this)); register(constructor.apply(this));
} }
registerModdedKeybinds(); registerModdedKeybinds();
@ -342,37 +343,44 @@ public class ControllerBindings<T extends ControllerState> {
ControlifyEvents.INPUT_MODE_CHANGED.register(mode -> KeyMapping.releaseAll()); ControlifyEvents.INPUT_MODE_CHANGED.register(mode -> KeyMapping.releaseAll());
} }
public ControllerBinding<T> register(ControllerBinding<T> binding) { public ControllerBinding register(ControllerBinding binding) {
registry.put(binding.id(), binding); registry.put(binding.id(), binding);
return binding; return binding;
} }
private ControllerBinding<?> create(UnaryOperator<ControllerBindingBuilder<?>> builder) { private ControllerBinding create(UnaryOperator<ControllerBindingBuilder<?>> builder) {
return builder.apply(ControllerBindingBuilder.create(controller)).build(); return builder.apply(ControllerBindingBuilder.create(controller)).build();
} }
@Deprecated @Deprecated
private ControllerBinding<?> create(GamepadBinds bind, ResourceLocation id) { private ControllerBinding create(GamepadBinds bind, ResourceLocation id) {
return new ControllerBinding<>(controller, bind, id); return ControllerBindingBuilder.create(controller)
.identifier(id)
.defaultBind(bind)
.build();
} }
@Deprecated @Deprecated
private ControllerBinding<?> create(GamepadBinds bind, ResourceLocation id, KeyMapping override, BooleanSupplier toggleOverride) { private ControllerBinding create(GamepadBinds bind, ResourceLocation id, KeyMapping override, BooleanSupplier toggleOverride) {
return new ControllerBinding<>(controller, bind, id, override, toggleOverride); return ControllerBindingBuilder.create(controller)
.identifier(id)
.defaultBind(bind)
.vanillaOverride(override, toggleOverride)
.build();
} }
public ControllerBinding<T> get(ResourceLocation id) { public ControllerBinding get(ResourceLocation id) {
return registry.get(id); return registry.get(id);
} }
public Map<ResourceLocation, ControllerBinding<T>> registry() { public Map<ResourceLocation, ControllerBinding> registry() {
return Collections.unmodifiableMap(registry); return Collections.unmodifiableMap(registry);
} }
public JsonObject toJson() { public JsonObject toJson() {
JsonObject json = new JsonObject(); JsonObject json = new JsonObject();
for (var binding : registry().values()) { for (var binding : registry().values()) {
json.add(binding.id().toString(), binding.currentBind().toJson()); json.add(binding.id().toString(), binding.toJson());
} }
return json; return json;
} }
@ -392,7 +400,7 @@ public class ControllerBindings<T extends ControllerState> {
clean = false; clean = false;
continue; continue;
} }
binding.setCurrentBind(IBind.fromJson(bind, controller)); ((ControllerBindingImpl<T>) binding).setCurrentBind(IBind.fromJson(bind, controller));
} }
return clean; return clean;
@ -416,7 +424,7 @@ public class ControllerBindings<T extends ControllerState> {
toggleOverride = ((ToggleKeyMappingAccessor) toggleKeyMapping).getNeedsToggle(); toggleOverride = ((ToggleKeyMappingAccessor) toggleKeyMapping).getNeedsToggle();
} }
ControllerBinding<T> binding = ControllerBindingBuilder.create(controller) ControllerBinding binding = ControllerBindingBuilder.create(controller)
.identifier(identifier) .identifier(identifier)
.defaultBind(new EmptyBind<>()) .defaultBind(new EmptyBind<>())
.name(Component.translatable(keyMapping.getName())) .name(Component.translatable(keyMapping.getName()))
@ -433,7 +441,7 @@ public class ControllerBindings<T extends ControllerState> {
} }
private void imitateVanillaClick() { private void imitateVanillaClick() {
ControllerBinding.clearPressedBinds(controller); ControllerBindingImpl.clearPressedBinds(controller);
if (Controlify.instance().currentInputMode() != InputMode.CONTROLLER) if (Controlify.instance().currentInputMode() != InputMode.CONTROLLER)
return; return;
@ -463,21 +471,21 @@ public class ControllerBindings<T extends ControllerState> {
public static final Api INSTANCE = new Api(); public static final Api INSTANCE = new Api();
@Override @Override
public BindingSupplier registerBind(ResourceLocation id, UnaryOperator<ControllerBindingBuilder<?>> builder) { public dev.isxander.controlify.api.bind.BindingSupplier registerBind(ResourceLocation id, UnaryOperator<ControllerBindingBuilder<?>> builder) {
CUSTOM_BINDS.put(id, bindings -> bindings.create(b -> builder.apply(b).identifier(id))); CUSTOM_BINDS.put(id, bindings -> bindings.create(b -> builder.apply(b).identifier(id)));
return controller -> controller.bindings().get(id); return controller -> controller.bindings().get(id);
} }
@Deprecated @Deprecated
@Override @Override
public BindingSupplier registerBind(GamepadBinds bind, ResourceLocation id) { public dev.isxander.controlify.api.bind.BindingSupplier registerBind(GamepadBinds bind, ResourceLocation id) {
CUSTOM_BINDS.put(id, bindings -> bindings.create(bind, id)); CUSTOM_BINDS.put(id, bindings -> bindings.create(bind, id));
return controller -> controller.bindings().get(id); return controller -> controller.bindings().get(id);
} }
@Deprecated @Deprecated
@Override @Override
public BindingSupplier registerBind(GamepadBinds bind, ResourceLocation id, KeyMapping override, BooleanSupplier toggleOverride) { public dev.isxander.controlify.api.bind.BindingSupplier registerBind(GamepadBinds bind, ResourceLocation id, KeyMapping override, BooleanSupplier toggleOverride) {
CUSTOM_BINDS.put(id, bindings -> bindings.create(bind, id, override, toggleOverride)); CUSTOM_BINDS.put(id, bindings -> bindings.create(bind, id, override, toggleOverride));
return controller -> controller.bindings().get(id); return controller -> controller.bindings().get(id);
} }

View File

@ -2,7 +2,8 @@ package dev.isxander.controlify.config.gui;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import dev.isxander.controlify.Controlify; import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.bindings.ControllerBinding; import dev.isxander.controlify.api.bind.ControllerBinding;
import dev.isxander.controlify.bindings.ControllerBindingImpl;
import dev.isxander.controlify.bindings.IBind; import dev.isxander.controlify.bindings.IBind;
import dev.isxander.controlify.config.GlobalSettings; import dev.isxander.controlify.config.GlobalSettings;
import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.controller.Controller;
@ -387,9 +388,10 @@ public class YACLHelper {
groupBindings(gamepad.bindings().registry().values()).forEach((categoryName, bindGroup) -> { groupBindings(gamepad.bindings().registry().values()).forEach((categoryName, bindGroup) -> {
controlsGroup.option(LabelOption.create(categoryName)); controlsGroup.option(LabelOption.create(categoryName));
for (var binding : bindGroup) { for (var binding : bindGroup) {
ControllerBindingImpl<GamepadState> bindingImpl = (ControllerBindingImpl<GamepadState>) binding;
controlsGroup.option(Option.createBuilder((Class<IBind<GamepadState>>) (Class<?>) IBind.class) controlsGroup.option(Option.createBuilder((Class<IBind<GamepadState>>) (Class<?>) IBind.class)
.name(binding.name()) .name(binding.name())
.binding(binding.defaultBind(), binding::currentBind, binding::setCurrentBind) .binding(bindingImpl.defaultBind(), bindingImpl::currentBind, bindingImpl::setCurrentBind)
.controller(opt -> new GamepadBindController(opt, gamepad)) .controller(opt -> new GamepadBindController(opt, gamepad))
.tooltip(binding.description()) .tooltip(binding.description())
.build()); .build());
@ -399,9 +401,10 @@ public class YACLHelper {
groupBindings(joystick.bindings().registry().values()).forEach((categoryName, bindGroup) -> { groupBindings(joystick.bindings().registry().values()).forEach((categoryName, bindGroup) -> {
controlsGroup.option(LabelOption.create(categoryName)); controlsGroup.option(LabelOption.create(categoryName));
for (var binding : bindGroup) { for (var binding : bindGroup) {
ControllerBindingImpl<JoystickState> bindingImpl = (ControllerBindingImpl<JoystickState>) binding;
controlsGroup.option(Option.createBuilder((Class<IBind<JoystickState>>) (Class<?>) IBind.class) controlsGroup.option(Option.createBuilder((Class<IBind<JoystickState>>) (Class<?>) IBind.class)
.name(binding.name()) .name(binding.name())
.binding(binding.defaultBind(), binding::currentBind, binding::setCurrentBind) .binding(bindingImpl.defaultBind(), bindingImpl::currentBind, bindingImpl::setCurrentBind)
.controller(opt -> new JoystickBindController(opt, joystick)) .controller(opt -> new JoystickBindController(opt, joystick))
.tooltip(binding.description()) .tooltip(binding.description())
.build()); .build());
@ -414,7 +417,7 @@ public class YACLHelper {
return category.build(); return category.build();
} }
private static <T extends ControllerState> Map<Component, List<ControllerBinding<T>>> groupBindings(Collection<ControllerBinding<T>> bindings) { private static <T extends ControllerState> Map<Component, List<ControllerBinding>> groupBindings(Collection<ControllerBinding> bindings) {
return bindings.stream() return bindings.stream()
.collect(Collectors.groupingBy(ControllerBinding::category, LinkedHashMap::new, Collectors.toList())); .collect(Collectors.groupingBy(ControllerBinding::category, LinkedHashMap::new, Collectors.toList()));
} }

View File

@ -1,11 +1,10 @@
package dev.isxander.controlify.gui; package dev.isxander.controlify.gui;
import dev.isxander.controlify.api.bind.ControllerBinding;
import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate; import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate;
import dev.isxander.controlify.api.buttonguide.ButtonRenderPosition; import dev.isxander.controlify.api.buttonguide.ButtonRenderPosition;
import dev.isxander.controlify.bindings.ControllerBinding;
import dev.isxander.controlify.bindings.ControllerBindings; import dev.isxander.controlify.bindings.ControllerBindings;
import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.client.gui.screens.Screen;
import java.util.function.Function; import java.util.function.Function;
@ -15,10 +14,10 @@ import java.util.function.Function;
public interface ButtonGuideRenderer<T extends AbstractButton> { public interface ButtonGuideRenderer<T extends AbstractButton> {
void setButtonGuide(RenderData<T> renderData); void setButtonGuide(RenderData<T> renderData);
static <T extends AbstractButton> void registerBindingForButton(T button, Function<ControllerBindings<?>, ControllerBinding<?>> binding, ButtonRenderPosition position, ButtonGuidePredicate<T> renderPredicate) { static <T extends AbstractButton> void registerBindingForButton(T button, Function<ControllerBindings<?>, ControllerBinding> binding, ButtonRenderPosition position, ButtonGuidePredicate<T> renderPredicate) {
((ButtonGuideRenderer<T>) button).setButtonGuide(new RenderData<>(binding, position, renderPredicate)); ((ButtonGuideRenderer<T>) button).setButtonGuide(new RenderData<>(binding, position, renderPredicate));
} }
record RenderData<T extends AbstractButton>(Function<ControllerBindings<?>, ControllerBinding<?>> binding, ButtonRenderPosition position, ButtonGuidePredicate<T> renderPredicate) { record RenderData<T extends AbstractButton>(Function<ControllerBindings<?>, ControllerBinding> binding, ButtonRenderPosition position, ButtonGuidePredicate<T> renderPredicate) {
} }
} }

View File

@ -1,14 +1,14 @@
package dev.isxander.controlify.ingame.guide; package dev.isxander.controlify.ingame.guide;
import dev.isxander.controlify.api.bind.ControllerBinding;
import dev.isxander.controlify.api.ingameguide.ActionLocation; import dev.isxander.controlify.api.ingameguide.ActionLocation;
import dev.isxander.controlify.api.ingameguide.ActionPriority; import dev.isxander.controlify.api.ingameguide.ActionPriority;
import dev.isxander.controlify.bindings.ControllerBinding;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public record GuideAction(ControllerBinding<?> binding, Component name, ActionLocation location, public record GuideAction(ControllerBinding binding, Component name, ActionLocation location,
ActionPriority priority) implements Comparable<GuideAction> { ActionPriority priority) implements Comparable<GuideAction> {
public GuideAction(ControllerBinding<?> binding, Component name, ActionLocation location) { public GuideAction(ControllerBinding binding, Component name, ActionLocation location) {
this(binding, name, location, ActionPriority.NORMAL); this(binding, name, location, ActionPriority.NORMAL);
} }

View File

@ -1,8 +1,9 @@
package dev.isxander.controlify.ingame.guide; package dev.isxander.controlify.ingame.guide;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.api.bind.ControllerBinding;
import dev.isxander.controlify.api.ingameguide.*; import dev.isxander.controlify.api.ingameguide.*;
import dev.isxander.controlify.bindings.ControllerBinding; import dev.isxander.controlify.bindings.ControllerBindingImpl;
import dev.isxander.controlify.compatibility.ControlifyCompat; import dev.isxander.controlify.compatibility.ControlifyCompat;
import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.api.event.ControlifyEvents; import dev.isxander.controlify.api.event.ControlifyEvents;
@ -47,15 +48,15 @@ public class InGameButtonGuide implements IngameGuideRegistry {
{ {
var offset = 0; var offset = 0;
for (var action : leftGuides) { for (var action : leftGuides) {
var bind = action.binding().currentBind(); var renderer = action.binding().renderer();
var drawSize = bind.drawSize(); var drawSize = renderer.size();
if (offset == 0) offset += drawSize.height() / 2; if (offset == 0) offset += drawSize.height() / 2;
int x = 4; int x = 4;
int y = 3 + offset; int y = 3 + offset;
bind.draw(poseStack, x, y); renderer.render(poseStack, x, y);
int textX = x + drawSize.width() + 2; int textX = x + drawSize.width() + 2;
int textY = y - minecraft.font.lineHeight / 2; int textY = y - minecraft.font.lineHeight / 2;
@ -69,15 +70,15 @@ public class InGameButtonGuide implements IngameGuideRegistry {
{ {
var offset = 0; var offset = 0;
for (var action : rightGuides) { for (var action : rightGuides) {
var bind = action.binding().currentBind(); var renderer = action.binding().renderer();
var drawSize = bind.drawSize(); var drawSize = renderer.size();
if (offset == 0) offset += drawSize.height() / 2; if (offset == 0) offset += drawSize.height() / 2;
int x = width - 4 - drawSize.width(); int x = width - 4 - drawSize.width();
int y = 3 + offset; int y = 3 + offset;
bind.draw(poseStack, x, y); renderer.render(poseStack, x, y);
int textX = x - minecraft.font.width(action.name()) - 2; int textX = x - minecraft.font.width(action.name()) - 2;
int textY = y - minecraft.font.lineHeight / 2; int textY = y - minecraft.font.lineHeight / 2;
@ -104,7 +105,7 @@ public class InGameButtonGuide implements IngameGuideRegistry {
continue; continue;
GuideAction guideAction = action.get(); GuideAction guideAction = action.get();
if (!guideAction.binding().unbound()) { if (!guideAction.binding().isUnbound()) {
if (action.get().location() == ActionLocation.LEFT) if (action.get().location() == ActionLocation.LEFT)
leftGuides.add(action.get()); leftGuides.add(action.get());
else else
@ -117,12 +118,12 @@ public class InGameButtonGuide implements IngameGuideRegistry {
} }
@Override @Override
public void registerGuideAction(ControllerBinding<?> binding, ActionLocation location, GuideActionNameSupplier supplier) { public void registerGuideAction(ControllerBinding binding, ActionLocation location, GuideActionNameSupplier supplier) {
this.registerGuideAction(binding, location, ActionPriority.NORMAL, supplier); this.registerGuideAction(binding, location, ActionPriority.NORMAL, supplier);
} }
@Override @Override
public void registerGuideAction(ControllerBinding<?> binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier supplier) { public void registerGuideAction(ControllerBinding binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier supplier) {
guidePredicates.add(new GuideActionSupplier(binding, location, priority, supplier)); guidePredicates.add(new GuideActionSupplier(binding, location, priority, supplier));
} }
@ -251,7 +252,7 @@ public class InGameButtonGuide implements IngameGuideRegistry {
} }
} }
private record GuideActionSupplier(ControllerBinding<?> binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier nameSupplier) { private record GuideActionSupplier(ControllerBinding binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier nameSupplier) {
public Optional<GuideAction> supply(Minecraft client, LocalPlayer player, ClientLevel level, HitResult hitResult, Controller<?, ?> controller) { public Optional<GuideAction> supply(Minecraft client, LocalPlayer player, ClientLevel level, HitResult hitResult, Controller<?, ?> controller) {
return nameSupplier.supply(new IngameGuideContext(client, player, level, hitResult, controller)) return nameSupplier.supply(new IngameGuideContext(client, player, level, hitResult, controller))
.map(name -> new GuideAction(binding, name, location, priority)); .map(name -> new GuideAction(binding, name, location, priority));

View File

@ -3,6 +3,7 @@ package dev.isxander.controlify.mixins.feature.guide.screen;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.Controlify; import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode; import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.api.bind.BindRenderer;
import dev.isxander.controlify.api.buttonguide.ButtonRenderPosition; import dev.isxander.controlify.api.buttonguide.ButtonRenderPosition;
import dev.isxander.controlify.bindings.IBind; import dev.isxander.controlify.bindings.IBind;
import dev.isxander.controlify.gui.ButtonGuideRenderer; import dev.isxander.controlify.gui.ButtonGuideRenderer;
@ -31,18 +32,18 @@ public abstract class AbstractButtonMixin extends AbstractWidgetMixin implements
private void renderButtonGuide(PoseStack matrices, Font renderer, int color, CallbackInfo ci) { private void renderButtonGuide(PoseStack matrices, Font renderer, int color, CallbackInfo ci) {
if (shouldRender()) { if (shouldRender()) {
switch (renderData.position()) { switch (renderData.position()) {
case LEFT -> getBind().draw(matrices, getX() - getBind().drawSize().width() - 1, getY() + getHeight() / 2); case LEFT -> getBind().render(matrices, getX() - getBind().size().width() - 1, getY() + getHeight() / 2);
case RIGHT -> getBind().draw(matrices, getX() + getWidth() + 1, getY() + getHeight() / 2); case RIGHT -> getBind().render(matrices, getX() + getWidth() + 1, getY() + getHeight() / 2);
case TEXT -> { case TEXT -> {
Font font = Minecraft.getInstance().font; Font font = Minecraft.getInstance().font;
int x; int x;
if (font.width(getMessage()) > getWidth()) { if (font.width(getMessage()) > getWidth()) {
x = getX(); x = getX();
} else { } else {
x = getX() + getWidth() / 2 - font.width(getMessage()) / 2 - getBind().drawSize().width(); x = getX() + getWidth() / 2 - font.width(getMessage()) / 2 - getBind().size().width();
} }
getBind().draw(matrices, x, getY() + getHeight() / 2); getBind().render(matrices, x, getY() + getHeight() / 2);
} }
} }
} }
@ -52,7 +53,7 @@ public abstract class AbstractButtonMixin extends AbstractWidgetMixin implements
private void shiftXOffset(PoseStack matrices, Font renderer, int color, CallbackInfo ci) { private void shiftXOffset(PoseStack matrices, Font renderer, int color, CallbackInfo ci) {
matrices.pushPose(); matrices.pushPose();
if (!shouldRender() || Minecraft.getInstance().font.width(getMessage()) > getWidth() || renderData.position() != ButtonRenderPosition.TEXT) return; if (!shouldRender() || Minecraft.getInstance().font.width(getMessage()) > getWidth() || renderData.position() != ButtonRenderPosition.TEXT) return;
matrices.translate(getBind().drawSize().width() / 2f, 0, 0); matrices.translate(getBind().size().width() / 2f, 0, 0);
} }
@Inject(method = "renderString", at = @At("RETURN")) @Inject(method = "renderString", at = @At("RETURN"))
@ -63,7 +64,7 @@ public abstract class AbstractButtonMixin extends AbstractWidgetMixin implements
@Override @Override
protected int shiftDrawSize(int x) { protected int shiftDrawSize(int x) {
if (!shouldRender() || Minecraft.getInstance().font.width(getMessage()) < getWidth() || renderData.position() != ButtonRenderPosition.TEXT) return x; if (!shouldRender() || Minecraft.getInstance().font.width(getMessage()) < getWidth() || renderData.position() != ButtonRenderPosition.TEXT) return x;
return x + getBind().drawSize().width(); return x + getBind().size().width();
} }
@Override @Override
@ -76,11 +77,11 @@ public abstract class AbstractButtonMixin extends AbstractWidgetMixin implements
&& this.isActive() && this.isActive()
&& Controlify.instance().currentInputMode() == InputMode.CONTROLLER && Controlify.instance().currentInputMode() == InputMode.CONTROLLER
&& Controlify.instance().currentController().config().showScreenGuide && Controlify.instance().currentController().config().showScreenGuide
&& !renderData.binding().apply(Controlify.instance().currentController().bindings()).unbound() && !renderData.binding().apply(Controlify.instance().currentController().bindings()).isUnbound()
&& renderData.renderPredicate().shouldDisplay((AbstractButton) (Object) this); && renderData.renderPredicate().shouldDisplay((AbstractButton) (Object) this);
} }
private IBind<?> getBind() { private BindRenderer getBind() {
return renderData.binding().apply(Controlify.instance().currentController().bindings()).currentBind(); return renderData.binding().apply(Controlify.instance().currentController().bindings()).renderer();
} }
} }

View File

@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.Controlify; import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode; import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.api.bind.BindRenderer;
import dev.isxander.controlify.bindings.IBind; import dev.isxander.controlify.bindings.IBind;
import dev.isxander.controlify.compatibility.ControlifyCompat; import dev.isxander.controlify.compatibility.ControlifyCompat;
import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.controller.Controller;
@ -39,18 +40,18 @@ public class TabNavigationBarMixin {
TabButton firstTab = tabButtons.get(0); TabButton firstTab = tabButtons.get(0);
TabButton lastTab = tabButtons.get(tabButtons.size() - 1); TabButton lastTab = tabButtons.get(tabButtons.size() - 1);
IBind<?> prevBind = controller.bindings().GUI_PREV_TAB.currentBind(); BindRenderer prevBind = controller.bindings().GUI_PREV_TAB.renderer();
DrawSize prevBindDrawSize = prevBind.drawSize(); DrawSize prevBindDrawSize = prevBind.size();
int firstButtonX = Math.max(firstTab.getX() - 2 - prevBindDrawSize.width(), firstTab.getX() / 2 - prevBindDrawSize.width() / 2); int firstButtonX = Math.max(firstTab.getX() - 2 - prevBindDrawSize.width(), firstTab.getX() / 2 - prevBindDrawSize.width() / 2);
int firstButtonY = 12; int firstButtonY = 12;
prevBind.draw(matrices, firstButtonX, firstButtonY); prevBind.render(matrices, firstButtonX, firstButtonY);
IBind<?> nextBind = controller.bindings().GUI_NEXT_TAB.currentBind(); BindRenderer nextBind = controller.bindings().GUI_NEXT_TAB.renderer();
DrawSize nextBindDrawSize = nextBind.drawSize(); DrawSize nextBindDrawSize = nextBind.size();
int lastButtonEnd = lastTab.getX() + lastTab.getWidth(); int lastButtonEnd = lastTab.getX() + lastTab.getWidth();
int lastButtonX = Math.min(lastTab.getX() + lastTab.getWidth() + 2, lastButtonEnd + (width - lastButtonEnd) / 2 - nextBindDrawSize.width() / 2); int lastButtonX = Math.min(lastTab.getX() + lastTab.getWidth() + 2, lastButtonEnd + (width - lastButtonEnd) / 2 - nextBindDrawSize.width() / 2);
int lastButtonY = 12; int lastButtonY = 12;
nextBind.draw(matrices, lastButtonX, lastButtonY); nextBind.render(matrices, lastButtonX, lastButtonY);
ControlifyCompat.ifEndHudBatching(); ControlifyCompat.ifEndHudBatching();
} }

View File

@ -2,14 +2,12 @@ package dev.isxander.controlify.test;
import dev.isxander.controlify.InputMode; import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.api.ControlifyApi;
import dev.isxander.controlify.api.bind.BindingSupplier;
import dev.isxander.controlify.api.bind.ControlifyBindingsApi; import dev.isxander.controlify.api.bind.ControlifyBindingsApi;
import dev.isxander.controlify.api.event.ControlifyEvents; import dev.isxander.controlify.api.event.ControlifyEvents;
import dev.isxander.controlify.bindings.BindingSupplier;
import dev.isxander.controlify.bindings.EmptyBind; import dev.isxander.controlify.bindings.EmptyBind;
import dev.isxander.controlify.bindings.GamepadBinds;
import dev.isxander.controlify.screenop.ScreenProcessor; import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.ScreenProcessorProvider; import dev.isxander.controlify.screenop.ScreenProcessorProvider;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@ -39,7 +37,7 @@ public class ControlifyTests {
@Test.TitleScreen("BindingSupplier getter test") @Test.TitleScreen("BindingSupplier getter test")
void bindingSupplierGetterTest() { void bindingSupplierGetterTest() {
var controller = createAndUseDummyController(); var controller = createAndUseDummyController();
assertNotNull(binding.get(controller), "Bind registry failed - Bind for fake controller is null"); assertNotNull(binding.onController(controller), "Bind registry failed - Bind for fake controller is null");
controller.finish(); controller.finish();
} }