Central sta.. Tab, Enter.

- Added auto-completion functionality to schedules' destination editor
This commit is contained in:
simibubi 2022-05-06 00:23:25 +02:00
parent b85c0ed93f
commit 48dcf583c1
5 changed files with 202 additions and 22 deletions

View file

@ -45,6 +45,8 @@ public class GlobalStation extends SingleTileEdgePoint {
super.read(buffer);
name = buffer.readUtf();
assembling = buffer.readBoolean();
if (buffer.readBoolean())
tilePos = buffer.readBlockPos();
}
@Override
@ -59,6 +61,9 @@ public class GlobalStation extends SingleTileEdgePoint {
super.write(buffer);
buffer.writeUtf(name);
buffer.writeBoolean(assembling);
buffer.writeBoolean(tilePos != null);
if (tilePos != null)
buffer.writeBlockPos(tilePos);
}
public boolean canApproachFrom(TrackNode side) {

View file

@ -0,0 +1,85 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import java.util.ArrayList;
import java.util.List;
import com.mojang.brigadier.context.StringRange;
import com.mojang.brigadier.suggestion.Suggestion;
import com.simibubi.create.foundation.utility.IntAttached;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.CommandSuggestions;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.util.Mth;
public class DestinationSuggestions extends CommandSuggestions {
private EditBox textBox;
private List<IntAttached<String>> viableStations;
private String previous = "<>";
private Font font;
private boolean active;
List<Suggestion> currentSuggestions;
private int yOffset;
public DestinationSuggestions(Minecraft pMinecraft, Screen pScreen, EditBox pInput, Font pFont,
List<IntAttached<String>> viableStations, int yOffset) {
super(pMinecraft, pScreen, pInput, pFont, true, true, 0, 7, false, 0xee_303030);
this.textBox = pInput;
this.font = pFont;
this.viableStations = viableStations;
this.yOffset = yOffset;
currentSuggestions = new ArrayList<>();
active = false;
}
public void tick() {
if (suggestions == null)
textBox.setSuggestion("");
if (active == textBox.isFocused())
return;
active = textBox.isFocused();
updateCommandInfo();
}
@Override
public void updateCommandInfo() {
String value = this.textBox.getValue();
if (value.equals(previous))
return;
if (!active) {
suggestions = null;
return;
}
previous = value;
currentSuggestions = viableStations.stream()
.filter(ia -> !ia.getValue()
.equals(value) && ia.getValue()
.toLowerCase()
.startsWith(value.toLowerCase()))
.sorted((ia1, ia2) -> Integer.compare(ia1.getFirst(), ia2.getFirst()))
.map(IntAttached::getValue)
.map(s -> new Suggestion(new StringRange(0, s.length()), s))
.toList();
showSuggestions(false);
}
public void showSuggestions(boolean pNarrateFirstSuggestion) {
if (currentSuggestions.isEmpty()) {
suggestions = null;
return;
}
int width = 0;
for (Suggestion suggestion : currentSuggestions)
width = Math.max(width, this.font.width(suggestion.getText()));
int x = Mth.clamp(textBox.getScreenX(0), 0, textBox.getScreenX(0) + textBox.getInnerWidth() - width);
suggestions = new CommandSuggestions.SuggestionsList(x, 72 + yOffset, width, currentSuggestions, false);
}
}

View file

@ -2,8 +2,11 @@ package com.simibubi.create.content.logistics.trains.management.schedule;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@ -16,6 +19,11 @@ import com.mojang.blaze3d.platform.InputConstants;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.GlobalRailwayManager;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation;
import com.simibubi.create.content.logistics.trains.management.schedule.condition.ScheduleWaitCondition;
import com.simibubi.create.content.logistics.trains.management.schedule.condition.ScheduledDelay;
import com.simibubi.create.content.logistics.trains.management.schedule.condition.TimedWaitCondition.TimeUnit;
@ -33,6 +41,7 @@ import com.simibubi.create.foundation.gui.widget.Indicator.State;
import com.simibubi.create.foundation.gui.widget.Label;
import com.simibubi.create.foundation.gui.widget.SelectionScrollInput;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.IntAttached;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
@ -53,6 +62,7 @@ import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.client.gui.GuiUtils;
public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContainer> {
@ -84,6 +94,8 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
private List<Pair<GuiEventListener, BiConsumer<IScheduleInput, GuiEventListener>>> editorSubWidgets;
private List<Integer> editorDividers;
private DestinationSuggestions destinationSuggestions;
public ScheduleScreen(ScheduleContainer container, Inventory inv, Component title) {
super(container, inv, title);
schedule = new Schedule();
@ -225,6 +237,11 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
addRenderableWidget(editorDelete);
}
private void onDestinationEdited(String text) {
if (destinationSuggestions != null)
destinationSuggestions.updateCommandInfo();
}
protected void stopEditing() {
confirmButton.visible = true;
cyclicButton.visible = true;
@ -235,6 +252,8 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
if (editingCondition == null && editingDestination == null)
return;
destinationSuggestions = null;
removeWidget(scrollInput);
removeWidget(scrollInputLabel);
removeWidget(editorConfirm);
@ -259,6 +278,7 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
}
protected void updateEditorSubwidgets(IScheduleInput field) {
destinationSuggestions = null;
menu.targetSlotActive = field.needsSlot();
editorSubWidgets.forEach(p -> removeWidget(p.getFirst()));
editorSubWidgets.clear();
@ -268,6 +288,17 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
if (editorSubWidgets.isEmpty())
editorDividers = null;
if (field instanceof DestinationInstruction) {
EditBox destinationBox = (EditBox) editorSubWidgets.get(0)
.getFirst();
destinationSuggestions = new DestinationSuggestions(this.minecraft, this, destinationBox, this.font,
getViableStations(field), topPos + 33);
destinationSuggestions.setAllowSuggestions(true);
destinationSuggestions.updateCommandInfo();
destinationBox.setResponder(this::onDestinationEdited);
}
editorSubWidgets.forEach(pair -> {
GuiEventListener e = pair.getFirst();
if (e instanceof AbstractSimiWidget)
@ -277,6 +308,42 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
});
}
private List<IntAttached<String>> getViableStations(IScheduleInput field) {
GlobalRailwayManager railwayManager = Create.RAILWAYS.sided(null);
Set<TrackGraph> viableGraphs = new HashSet<>(railwayManager.trackNetworks.values());
for (ScheduleEntry entry : schedule.entries) {
if (!(entry.instruction instanceof DestinationInstruction destination))
continue;
if (destination == field)
continue;
String filter = destination.getFilter()
.replace("*", ".*");
if (filter.isBlank())
continue;
Graphs: for (Iterator<TrackGraph> iterator = viableGraphs.iterator(); iterator.hasNext();) {
TrackGraph trackGraph = iterator.next();
for (GlobalStation station : trackGraph.getPoints(EdgePointType.STATION)) {
if (station.name.matches(filter))
continue Graphs;
}
iterator.remove();
}
}
Vec3 position = minecraft.player.position();
Set<String> visited = new HashSet<>();
return viableGraphs.stream()
.flatMap(g -> g.getPoints(EdgePointType.STATION)
.stream())
.filter(station -> station.tilePos != null)
.filter(station -> visited.add(station.name))
.map(station -> IntAttached.with((int) Vec3.atBottomCenterOf(station.tilePos)
.distanceTo(position), station.name))
.toList();
}
@Override
protected void containerTick() {
super.containerTick();
@ -284,6 +351,9 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
for (LerpedFloat lerpedFloat : horizontalScrolls)
lerpedFloat.tickChaser();
if (destinationSuggestions != null)
destinationSuggestions.tick();
schedule.savedProgress =
schedule.entries.isEmpty() ? 0 : Mth.clamp(schedule.savedProgress, 0, schedule.entries.size() - 1);
resetProgress.active = schedule.savedProgress > 0;
@ -813,6 +883,9 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
@Override
public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) {
if (destinationSuggestions != null
&& destinationSuggestions.mouseClicked((int) pMouseX, (int) pMouseY, pButton))
return true;
if (editorConfirm != null && editorConfirm.isMouseOver(pMouseX, pMouseY) && onEditorClose != null) {
onEditorClose.accept(true);
stopEditing();
@ -831,6 +904,8 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
@Override
public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) {
if (destinationSuggestions != null && destinationSuggestions.keyPressed(pKeyCode, pScanCode, pModifiers))
return true;
if (editingCondition == null && editingDestination == null)
return super.keyPressed(pKeyCode, pScanCode, pModifiers);
InputConstants.Key mouseKey = InputConstants.getKey(pKeyCode, pScanCode);
@ -846,6 +921,8 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
@Override
public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) {
if (destinationSuggestions != null && destinationSuggestions.mouseScrolled(Mth.clamp(pDelta, -1.0D, 1.0D)))
return true;
if (editingCondition != null || editingDestination != null)
return super.mouseScrolled(pMouseX, pMouseY, pDelta);
@ -910,7 +987,15 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
@Override
protected void renderForeground(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) {
if (destinationSuggestions != null) {
matrixStack.pushPose();
matrixStack.translate(0, 0, 500);
destinationSuggestions.render(matrixStack, mouseX, mouseY);
matrixStack.popPose();
}
super.renderForeground(matrixStack, mouseX, mouseY, partialTicks);
GuiGameElement.of(menu.contentHolder).<GuiGameElement
.GuiRenderBuilder>at(leftPos + AllGuiTextures.SCHEDULE.width, topPos + AllGuiTextures.SCHEDULE.height - 56,
-200)

View file

@ -197,6 +197,7 @@ public class TrackPlacement {
if (parallel) {
double[] sTest = VecHelper.intersect(end1, end2, normedAxis1, cross2, Axis.Y);
if (sTest != null) {
double t = Math.abs(sTest[0]);
double u = Math.abs(sTest[1]);
@ -228,6 +229,7 @@ public class TrackPlacement {
}
}
}
}
// Slope

View file

@ -38,3 +38,6 @@ public net.minecraft.client.model.AgeableListModel f_170339_ # babyZHeadOffset
public net.minecraft.client.model.AgeableListModel f_102010_ # babyHeadScale
public net.minecraft.client.model.AgeableListModel f_102011_ # babyBodyScale
public net.minecraft.client.model.AgeableListModel f_102012_ # bodyYOffset
public net.minecraft.client.gui.components.CommandSuggestions f_93866_ # suggestions
public net.minecraft.client.gui.components.CommandSuggestions$SuggestionsList <init>(Lnet/minecraft/client/gui/components/CommandSuggestions;IIILjava/util/List;Z)V # <init>