package com.replaymod.pathing.gui; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.SettableFuture; import com.replaymod.core.ReplayMod; import com.replaymod.core.utils.Utils; import com.replaymod.replay.ReplayModReplay; import com.replaymod.replaystudio.pathing.PathingRegistry; import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.Timeline; import com.replaymod.replaystudio.replay.ReplayFile; import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.container.AbstractGuiClickableContainer; import de.johni0702.minecraft.gui.container.GuiContainer; import de.johni0702.minecraft.gui.container.GuiPanel; import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiVerticalList; import de.johni0702.minecraft.gui.element.GuiButton; import de.johni0702.minecraft.gui.element.GuiLabel; import de.johni0702.minecraft.gui.element.GuiTextField; import de.johni0702.minecraft.gui.element.IGuiButton; import de.johni0702.minecraft.gui.function.Closeable; import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.popup.GuiYesNoPopup; import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Consumer; import org.lwjgl.util.Dimension; import org.lwjgl.util.ReadableDimension; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; /** * Gui for loading and saving {@link Timeline Timelines}. */ public class GuiKeyframeRepository extends GuiScreen implements Closeable { public final GuiPanel contentPanel = new GuiPanel(this).setBackgroundColor(Colors.DARK_TRANSPARENT); public final GuiLabel title = new GuiLabel(contentPanel).setI18nText("replaymod.gui.keyframerepository.title"); public final GuiVerticalList list = new GuiVerticalList(contentPanel).setDrawShadow(true).setDrawSlider(true); public final GuiPanel buttonPanel = new GuiPanel(contentPanel).setLayout(new HorizontalLayout().setSpacing(5)); public final GuiButton overwriteButton = new GuiButton(buttonPanel).onClick(new Runnable() { @Override public void run() { GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this, new GuiLabel().setI18nText("replaymod.gui.keyframerepo.overwrite").setColor(Colors.BLACK) ).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no"); Utils.addCallback(popup.getFuture(), doIt -> { if (doIt) { timelines.put(selectedEntry.name, currentTimeline); overwriteButton.setDisabled(); save(); } }, Throwable::printStackTrace); } }).setSize(75, 20).setI18nLabel("replaymod.gui.overwrite").setDisabled(); public final GuiButton saveAsButton = new GuiButton(buttonPanel).onClick(new Runnable() { @Override public void run() { final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true); final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this, new GuiLabel().setI18nText("replaymod.gui.saveas").setColor(Colors.BLACK), nameField ).setYesI18nLabel("replaymod.gui.save").setNoI18nLabel("replaymod.gui.cancel"); popup.getYesButton().setDisabled(); ((VerticalLayout) popup.getInfo().getLayout()).setSpacing(7); nameField.onEnter(new Runnable() { @Override public void run() { if (popup.getYesButton().isEnabled()) { popup.getYesButton().onClick(); } } }).onTextChanged(new Consumer() { @Override public void consume(String obj) { popup.getYesButton().setEnabled(!nameField.getText().isEmpty() && !timelines.containsKey(nameField.getText())); } }); Futures.addCallback(popup.getFuture(), new FutureCallback() { @Override public void onSuccess(Boolean save) { if (save) { String name = nameField.getText(); timelines.put(name, currentTimeline); list.getListPanel().addElements(null, new Entry(name)); save(); } } @Override public void onFailure(Throwable t) { t.printStackTrace(); } }); } }).setSize(75, 20).setI18nLabel("replaymod.gui.saveas"); public final GuiButton loadButton = new GuiButton(buttonPanel).onClick(new Runnable() { @Override public void run() { getMinecraft().displayGuiScreen(null); try { Timeline timeline = timelines.get(selectedEntry.name); for (Path path : timeline.getPaths()) { path.updateAll(); } future.set(timeline); } catch (Throwable t) { future.setException(t); } } }).setSize(75, 20).setI18nLabel("replaymod.gui.load").setDisabled(); public final GuiButton renameButton = new GuiButton(buttonPanel).onClick(new Runnable() { @Override public void run() { final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true).setText(selectedEntry.name); final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this, new GuiLabel().setI18nText("replaymod.gui.rename").setColor(Colors.BLACK), nameField ).setYesI18nLabel("replaymod.gui.done").setNoI18nLabel("replaymod.gui.cancel"); popup.getYesButton().setDisabled(); ((VerticalLayout) popup.getInfo().getLayout()).setSpacing(7); nameField.onEnter(new Runnable() { @Override public void run() { if (popup.getYesButton().isEnabled()) { popup.getYesButton().onClick(); } } }).onTextChanged(new Consumer() { @Override public void consume(String obj) { popup.getYesButton().setEnabled(!nameField.getText().isEmpty() && !timelines.containsKey(nameField.getText())); } }); Futures.addCallback(popup.getFuture(), new FutureCallback() { @Override public void onSuccess(Boolean save) { if (save) { String name = nameField.getText(); timelines.put(name, timelines.remove(selectedEntry.name)); selectedEntry.name = name; selectedEntry.label.setText(name); save(); } } @Override public void onFailure(Throwable t) { t.printStackTrace(); } }); } }).setSize(75, 20).setI18nLabel("replaymod.gui.rename").setDisabled(); public final GuiButton removeButton = new GuiButton(buttonPanel).onClick(new Runnable() { @Override public void run() { GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this, new GuiLabel().setI18nText("replaymod.gui.keyframerepo.delete").setColor(Colors.BLACK) ).setYesI18nLabel("replaymod.gui.delete").setNoI18nLabel("replaymod.gui.cancel"); Futures.addCallback(popup.getFuture(), new FutureCallback() { @Override public void onSuccess(Boolean delete) { if (delete) { timelines.remove(selectedEntry.name); list.getListPanel().removeElement(selectedEntry); selectedEntry = null; overwriteButton.setDisabled(); loadButton.setDisabled(); renameButton.setDisabled(); removeButton.setDisabled(); save(); } } @Override public void onFailure(Throwable t) { t.printStackTrace(); } }); } }).setSize(75, 20).setI18nLabel("replaymod.gui.remove").setDisabled(); private final Map timelines = new LinkedHashMap<>(); private final Timeline currentTimeline; private final SettableFuture future = SettableFuture.create(); private final PathingRegistry registry; private final ReplayFile replayFile; private Entry selectedEntry; { setBackground(Background.NONE); setLayout(new CustomLayout() { @Override protected void layout(GuiScreen container, int width, int height) { pos(contentPanel, width / 2 - width(contentPanel) / 2, height / 2 - height(contentPanel) / 2); } }); contentPanel.setLayout(new CustomLayout() { @Override protected void layout(GuiPanel container, int width, int height) { pos(title, width / 2 - width(title) / 2, 5); size(list, width, height - 10 - height(buttonPanel) - 10 - y(title) - height(title) - 5); pos(list, width / 2 - width(list) / 2, y(title) + height(title) + 5); pos(buttonPanel, width / 2 - width(buttonPanel) / 2, y(list) + height(list) + 10); } @Override public ReadableDimension calcMinSize(GuiContainer container) { ReadableDimension screenSize = getMinSize(); return new Dimension(screenSize.getWidth() - 10, screenSize.getHeight() - 10); } }); } public GuiKeyframeRepository(PathingRegistry registry, ReplayFile replayFile, Timeline currentTimeline) throws IOException { this.registry = registry; this.replayFile = replayFile; this.currentTimeline = currentTimeline; timelines.putAll(replayFile.getTimelines(registry)); for (Map.Entry entry : timelines.entrySet()) { list.getListPanel().addElements(null, new Entry(entry.getKey())); } } @Override public void display() { super.display(); ReplayModReplay.instance.getReplayHandler().getOverlay().setVisible(false); } @Override public void close() { ReplayModReplay.instance.getReplayHandler().getOverlay().setVisible(true); } public SettableFuture getFuture() { return future; } public void save() { try { replayFile.writeTimelines(registry, timelines); } catch (IOException e) { e.printStackTrace(); ReplayMod.instance.printWarningToChat("Error saving timelines: " + e.getMessage()); } } public class Entry extends AbstractGuiClickableContainer { public final GuiLabel label = new GuiLabel(this); private String name; public Entry(String name) { this.name = name; setLayout(new CustomLayout() { @Override protected void layout(Entry container, int width, int height) { pos(label, 5, height / 2 - height(label) / 2); } @Override public ReadableDimension calcMinSize(GuiContainer container) { return new Dimension(buttonPanel.calcMinSize().getWidth(), 16); } }); label.setText(name); } @Override protected void onClick() { selectedEntry = this; buttonPanel.forEach(IGuiButton.class).setEnabled(); } @Override public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) { if (selectedEntry == this) { renderer.drawRect(0, 0, size.getWidth(), size.getHeight(), Colors.BLACK); renderer.drawRect(0, 0, 2, size.getHeight(), Colors.WHITE); } super.draw(renderer, size, renderInfo); } @Override protected Entry getThis() { return this; } } }