From 51c073012e6bb8d032cf44bf58ebdb3ef8459638 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 12 Mar 2019 19:44:09 +0100 Subject: [PATCH] Re-work Replay Editor UI --- .../core/handler/MainMenuHandler.java | 4 +- .../com/replaymod/editor/ReplayModEditor.java | 2 +- .../replaymod/editor/gui/GuiEditReplay.java | 262 ++++++++++++++++++ .../replaymod/editor/gui/MarkerProcessor.java | 5 +- .../replaymod/editor/handler/GuiHandler.java | 68 +++-- .../replaymod/online/handler/GuiHandler.java | 9 +- .../recording/packet/PacketListener.java | 2 +- .../com/replaymod/replay/ReplayHandler.java | 24 +- .../com/replaymod/replay/ReplayModReplay.java | 15 +- .../gui/overlay/GuiEditMarkerPopup.java | 13 +- .../replay/gui/overlay/GuiMarkerTimeline.java | 86 +++++- .../replaymod/replay/handler/GuiHandler.java | 2 +- src/main/resources/assets/replaymod/lang | 2 +- 13 files changed, 396 insertions(+), 98 deletions(-) create mode 100644 src/main/java/com/replaymod/editor/gui/GuiEditReplay.java diff --git a/src/main/java/com/replaymod/core/handler/MainMenuHandler.java b/src/main/java/com/replaymod/core/handler/MainMenuHandler.java index 20b66dbd..42ec9dbb 100644 --- a/src/main/java/com/replaymod/core/handler/MainMenuHandler.java +++ b/src/main/java/com/replaymod/core/handler/MainMenuHandler.java @@ -40,9 +40,9 @@ public class MainMenuHandler { if (x(button) + button.width < gui.width / 2 - 100 || x(button) > gui.width / 2 + 100 || y(button) > gui.height / 4 + 10 + 4 * 24) continue; - // Move button up to make space for two rows of buttons + // Move button up to make space for one rows of buttons // and then move back down by 10 to compensate for the space to the exit button that was already there - int offset = -2 * 24 + 10; + int offset = -1 * 24 + 10; y(button, y(button) + offset); //#if MC>=11300 diff --git a/src/main/java/com/replaymod/editor/ReplayModEditor.java b/src/main/java/com/replaymod/editor/ReplayModEditor.java index 1b4dd02b..6e5b0304 100644 --- a/src/main/java/com/replaymod/editor/ReplayModEditor.java +++ b/src/main/java/com/replaymod/editor/ReplayModEditor.java @@ -23,7 +23,7 @@ public class ReplayModEditor implements Module { @Override public void initClient() { - new GuiHandler(this).register(); + new GuiHandler().register(); } public ReplayMod getCore() { diff --git a/src/main/java/com/replaymod/editor/gui/GuiEditReplay.java b/src/main/java/com/replaymod/editor/gui/GuiEditReplay.java new file mode 100644 index 00000000..9045f32c --- /dev/null +++ b/src/main/java/com/replaymod/editor/gui/GuiEditReplay.java @@ -0,0 +1,262 @@ +package com.replaymod.editor.gui; + +import com.replaymod.core.ReplayMod; +import com.replaymod.core.utils.Utils; +import com.replaymod.editor.ReplayModEditor; +import com.replaymod.replay.gui.overlay.GuiMarkerTimeline; +import com.replaymod.replaystudio.data.Marker; +import com.replaymod.replaystudio.replay.ZipReplayFile; +import com.replaymod.replaystudio.studio.ReplayStudio; +import de.johni0702.minecraft.gui.GuiRenderer; +import de.johni0702.minecraft.gui.container.GuiContainer; +import de.johni0702.minecraft.gui.container.GuiPanel; +import de.johni0702.minecraft.gui.element.GuiButton; +import de.johni0702.minecraft.gui.element.GuiHorizontalScrollbar; +import de.johni0702.minecraft.gui.element.GuiTexturedButton; +import de.johni0702.minecraft.gui.element.GuiTooltip; +import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar; +import de.johni0702.minecraft.gui.element.advanced.GuiTimelineTime; +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.AbstractGuiPopup; +import de.johni0702.minecraft.gui.utils.lwjgl.Color; +import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; +import net.minecraft.crash.CrashReport; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +public class GuiEditReplay extends AbstractGuiPopup { + private final Path inputPath; + + private final EditTimeline timeline; + + private final GuiTimelineTime timelineTime = new GuiTimelineTime<>(); + + private final GuiHorizontalScrollbar scrollbar = new GuiHorizontalScrollbar().setSize(300, 9); + + + private final GuiTexturedButton zoomInButton = new GuiTexturedButton().setSize(9, 9) + .onClick(() -> zoomTimeline(2d / 3d)) + .setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 20) + .setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomin")); + + private final GuiTexturedButton zoomOutButton = new GuiTexturedButton().setSize(9, 9) + .onClick(() -> zoomTimeline(3d / 2d)) + .setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 30) + .setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomout")); + + private final GuiPanel zoomButtonPanel = new GuiPanel() + .setLayout(new VerticalLayout(VerticalLayout.Alignment.CENTER).setSpacing(2)) + .addElements(null, zoomInButton, zoomOutButton); + + private Set markers; + + protected GuiEditReplay(GuiContainer container, Path inputPath) throws IOException { + super(container); + this.inputPath = inputPath; + + try (ZipReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), inputPath.toFile())) { + markers = replayFile.getMarkers().or(HashSet::new); + timeline = new EditTimeline(new HashSet<>(markers), markers -> this.markers = markers); + timeline.setSize(300, 20) + .setMarkers() + .setLength(replayFile.getMetaData().getDuration()) + .onClick(timeline::setCursorPosition); + } + + timelineTime.setTimeline(timeline); + + scrollbar.onValueChanged(() -> { + timeline.setOffset((int) (scrollbar.getPosition() * timeline.getLength())); + timeline.setZoom(scrollbar.getZoom()); + }).setZoom(1); + + GuiPanel timelinePanel = new GuiPanel() + .setSize(300, 40) + .setLayout(new CustomLayout() { + @Override + protected void layout(GuiPanel container, int width, int height) { + pos(zoomButtonPanel, width - width(zoomButtonPanel), 10); + pos(timelineTime, 0, 2); + size(timelineTime, x(zoomButtonPanel), 8); + pos(timeline, 0, y(timelineTime) + height(timelineTime)); + size(timeline, x(zoomButtonPanel) - 2, 20); + pos(scrollbar, 0, y(timeline) + height(timeline) + 1); + size(scrollbar, x(zoomButtonPanel) - 2, 9); + } + }) + .addElements(null, timelineTime, timeline, scrollbar, zoomButtonPanel); + + GuiButton buttonAddSplit = new GuiButton() + .setSize(100, 20) + .setI18nLabel("replaymod.gui.edit.split") + .setTooltip(new GuiTooltip().setI18nText("replaymod.gui.edit.split.tooltip")) + .onClick(() -> { + Marker marker = new Marker(); + marker.setTime(timeline.getCursorPosition()); + marker.setName(MarkerProcessor.MARKER_NAME_SPLIT); + timeline.addMarker(marker); + }); + + GuiButton buttonInsertCut = new GuiButton() + .setSize(100, 20) + .setI18nLabel("replaymod.gui.edit.cut.start") + .setTooltip(new GuiTooltip().setI18nText("replaymod.gui.edit.cut.tooltip")) + .onClick(() -> { + Marker marker = new Marker(); + marker.setTime(timeline.getCursorPosition()); + marker.setName(MarkerProcessor.MARKER_NAME_START_CUT); + timeline.addMarker(marker); + }); + + GuiButton buttonEndCut = new GuiButton() + .setSize(100, 20) + .setI18nLabel("replaymod.gui.edit.cut.end") + .setTooltip(new GuiTooltip().setI18nText("replaymod.gui.edit.cut.tooltip")) + .onClick(() -> { + Marker marker = new Marker(); + marker.setTime(timeline.getCursorPosition()); + marker.setName(MarkerProcessor.MARKER_NAME_END_CUT); + timeline.addMarker(marker); + }); + + GuiPanel controlPanel = new GuiPanel() + .setLayout(new HorizontalLayout().setSpacing(4)) + .addElements(null, buttonAddSplit, buttonInsertCut, buttonEndCut); + + GuiButton applyButton = new GuiButton().setI18nLabel("replaymod.gui.edit.apply").setSize(150, 20).onClick(this::apply); + GuiButton closeButton = new GuiButton().setI18nLabel("replaymod.gui.close").setSize(150, 20).onClick(this::close); + GuiPanel buttonPanel = new GuiPanel() + .setLayout(new HorizontalLayout().setSpacing(8)) + .addElements(null, applyButton, closeButton); + + popup.setLayout(new VerticalLayout(VerticalLayout.Alignment.TOP).setSpacing(10)); + popup.addElements(new VerticalLayout.Data(0.5), timelinePanel, controlPanel, buttonPanel); + } + + private void zoomTimeline(double factor) { + scrollbar.setZoom(scrollbar.getZoom() * factor); + } + + private void apply() { + ProgressPopup progressPopup = new ProgressPopup(this); + + new Thread(() -> { + try (ZipReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), inputPath.toFile())) { + replayFile.writeMarkers(markers); + replayFile.save(); + } catch (IOException e) { + Utils.error(ReplayModEditor.LOGGER, this, CrashReport.makeCrashReport(e, "Writing markers"), this::close); + } + + try { + MarkerProcessor.apply(inputPath, progressPopup.progressBar::setProgress); + } catch (IOException e) { + Utils.error(ReplayModEditor.LOGGER, this, CrashReport.makeCrashReport(e, "Running marker processor"), this::close); + } + + ReplayMod.instance.runLater(() -> { + progressPopup.close(); + close(); + }); + }).start(); + } + + @Override + public void open() { + super.open(); + } + + @Override + protected GuiEditReplay getThis() { + return this; + } + + private class ProgressPopup extends AbstractGuiPopup { + private final GuiProgressBar progressBar = new GuiProgressBar(popup).setSize(300, 20); + + ProgressPopup(GuiContainer container) { + super(container); + open(); + } + + @Override + public void close() { + super.close(); + } + + @Override + protected ProgressPopup getThis() { + return this; + } + } + + private class EditTimeline extends GuiMarkerTimeline { + EditTimeline(Set markers, Consumer> saveMarkers) { + super(markers, saveMarkers); + } + + @Override + protected void drawMarkers(GuiRenderer renderer, ReadableDimension size) { + drawCutQuads(renderer, size); + super.drawMarkers(renderer, size); + } + + @Override + protected void drawMarker(GuiRenderer renderer, ReadableDimension size, Marker marker, int markerX) { + if (MarkerProcessor.MARKER_NAME_SPLIT.equals(marker.getName())) { + int height = size.getHeight() - BORDER_BOTTOM - BORDER_TOP - MARKER_SIZE + 1; + for (int y = 0; y < height; y += 3) { + renderer.drawRect(markerX, BORDER_TOP + y, 1, 2, Color.WHITE); + } + } + + super.drawMarker(renderer, size, marker, markerX); + } + + private void drawCutQuads(GuiRenderer renderer, ReadableDimension size) { + boolean inCut = false; + int startTime = 0; + for (Marker marker : markers.stream().sorted(Comparator.comparing(Marker::getTime)).collect(Collectors.toList())) { + if (MarkerProcessor.MARKER_NAME_START_CUT.equals(marker.getName()) && !inCut) { + inCut = true; + startTime = marker.getTime(); + } else if (MarkerProcessor.MARKER_NAME_END_CUT.equals(marker.getName()) && inCut) { + drawCutQuad(renderer, size, startTime, marker.getTime()); + inCut = false; + } + } + + if (inCut) { + drawCutQuad(renderer, size, startTime, getLength()); + } + } + + private void drawCutQuad(GuiRenderer renderer, ReadableDimension size, int startFrameTime, int endFrameTime) { + int visibleWidth = size.getWidth() - BORDER_LEFT - BORDER_RIGHT; + int startTime = getOffset(); + int visibleTime = (int) (getZoom() * getLength()); + int endTime = getOffset() + visibleTime; + + if (startFrameTime >= endTime || endFrameTime <= startTime) { + return; // Segment out of display range + } + + double relativeStart = startFrameTime - startTime; + double relativeEnd = endFrameTime - startTime; + int startX = BORDER_LEFT + Math.max(0, (int) (relativeStart / visibleTime * visibleWidth) + MARKER_SIZE / 2 + 1); + int endX = BORDER_LEFT + Math.min(visibleWidth, (int) (relativeEnd / visibleTime * visibleWidth) - MARKER_SIZE / 2); + if (startX < endX) { + renderer.drawRect(startX + 1, size.getHeight() - BORDER_BOTTOM - MARKER_SIZE, + endX - startX - 2, MARKER_SIZE - 2, Color.RED); + } + } + } +} diff --git a/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java b/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java index a71da532..c804d575 100644 --- a/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java +++ b/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java @@ -26,6 +26,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import java.util.stream.Collectors; public class MarkerProcessor { @@ -49,7 +50,7 @@ public class MarkerProcessor { } } - public static void apply(Path path) throws IOException { + public static void apply(Path path, Consumer progress) throws IOException { if (!hasWork(path)) { return; } @@ -71,6 +72,7 @@ public class MarkerProcessor { Iterator markerIterator = markers.iterator(); boolean anySplit = markers.stream().anyMatch(m -> m.getName().equals(MARKER_NAME_SPLIT)); + int inputDuration = inputReplayFile.getMetaData().getDuration(); ReplayInputStream replayInputStream = inputReplayFile.getPacketData(studio, true); int timeOffset = 0; SquashFilter cutFilter = null; @@ -142,6 +144,7 @@ public class MarkerProcessor { duration = nextPacket.getTime() - timeOffset; } nextPacket = replayInputStream.readPacket(); + progress.accept((float) nextPacket.getTime() / (float) inputDuration); } } diff --git a/src/main/java/com/replaymod/editor/handler/GuiHandler.java b/src/main/java/com/replaymod/editor/handler/GuiHandler.java index 0a673790..20e30061 100644 --- a/src/main/java/com/replaymod/editor/handler/GuiHandler.java +++ b/src/main/java/com/replaymod/editor/handler/GuiHandler.java @@ -1,17 +1,19 @@ package com.replaymod.editor.handler; +import com.replaymod.core.utils.Utils; import com.replaymod.editor.ReplayModEditor; -import com.replaymod.editor.gui.GuiReplayEditor; +import com.replaymod.editor.gui.GuiEditReplay; +import com.replaymod.replay.gui.screen.GuiReplayViewer; +import de.johni0702.minecraft.gui.container.AbstractGuiScreen; +import de.johni0702.minecraft.gui.container.GuiPanel; import de.johni0702.minecraft.gui.container.GuiScreen; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.resources.I18n; +import de.johni0702.minecraft.gui.element.GuiElement; +import net.minecraft.crash.CrashReport; import net.minecraftforge.client.event.GuiScreenEvent; //#if MC>=10800 //#if MC>=11300 import net.minecraftforge.eventbus.api.SubscribeEvent; -import java.util.ArrayList; //#else //$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#endif @@ -19,48 +21,42 @@ import java.util.ArrayList; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //#endif +import java.io.IOException; + import static com.replaymod.core.versions.MCVer.*; public class GuiHandler { - private static final int BUTTON_REPLAY_EDITOR = 17890237; - - private final ReplayModEditor mod; - - public GuiHandler(ReplayModEditor mod) { - this.mod = mod; - } - public void register() { FML_BUS.register(this); FORGE_BUS.register(this); } @SubscribeEvent - public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) { - if (!(getGui(event) instanceof GuiMainMenu)) { + public void injectIntoReplayViewer(GuiScreenEvent.InitGuiEvent.Post event) { + AbstractGuiScreen guiScreen = GuiScreen.from(getGui(event)); + if (!(guiScreen instanceof GuiReplayViewer)) { return; } - - GuiButton button = new GuiButton(BUTTON_REPLAY_EDITOR, getGui(event).width / 2 + 2, - getGui(event).height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayeditor")) { - //#if MC>=11300 - @Override - public void onClick(double mouseX, double mouseY) { - onButton(new GuiScreenEvent.ActionPerformedEvent.Pre(getGui(event), this, new ArrayList<>())); - } - //#endif - }; - button.width = button.width / 2 - 2; - addButton(event, button); - } - - @SubscribeEvent - public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) { - if(!getButton(event).enabled) return; - - if (getGui(event) instanceof GuiMainMenu) { - if (getButton(event).id == BUTTON_REPLAY_EDITOR) { - new GuiReplayEditor(GuiScreen.wrap(getGui(event)), mod.getCore()).display(); + final GuiReplayViewer replayViewer = (GuiReplayViewer) guiScreen; + // Inject Edit button + for (GuiElement element : replayViewer.replayButtonPanel.getChildren()) { + if (element instanceof GuiPanel && (((GuiPanel) element).getChildren().isEmpty())) { + new de.johni0702.minecraft.gui.element.GuiButton((GuiPanel) element).onClick(new Runnable() { + @Override + public void run() { + try { + new GuiEditReplay(replayViewer, replayViewer.list.getSelected().file.toPath()) { + @Override + protected void close() { + super.close(); + replayViewer.list.load(); + } + }.open(); + } catch (IOException e) { + Utils.error(ReplayModEditor.LOGGER, replayViewer, CrashReport.makeCrashReport(e, "Opening replay editor"), () -> {}); + } + } + }).setSize(73, 20).setI18nLabel("replaymod.gui.replayeditor").setDisabled(); } } } diff --git a/src/main/java/com/replaymod/online/handler/GuiHandler.java b/src/main/java/com/replaymod/online/handler/GuiHandler.java index 7ac54ca5..bbba6549 100644 --- a/src/main/java/com/replaymod/online/handler/GuiHandler.java +++ b/src/main/java/com/replaymod/online/handler/GuiHandler.java @@ -49,8 +49,12 @@ public class GuiHandler { return; } - GuiButton button = new GuiButton(BUTTON_REPLAY_CENTER, getGui(event).width / 2 - 100, - getGui(event).height / 4 + 10 + 4 * 24, I18n.format("replaymod.gui.replaycenter")) { + GuiButton button = new GuiButton( + BUTTON_REPLAY_CENTER, + getGui(event).width / 2 + 2, + getGui(event).height / 4 + 10 + 4 * 24, + I18n.format("replaymod.gui.replaycenter") + ) { //#if MC>=11300 @Override public void onClick(double mouseX, double mouseY) { @@ -58,6 +62,7 @@ public class GuiHandler { } //#endif }; + button.width = button.width / 2 - 2; addButton(event, button); } diff --git a/src/main/java/com/replaymod/recording/packet/PacketListener.java b/src/main/java/com/replaymod/recording/packet/PacketListener.java index 44e9851f..dc3d8371 100755 --- a/src/main/java/com/replaymod/recording/packet/PacketListener.java +++ b/src/main/java/com/replaymod/recording/packet/PacketListener.java @@ -210,7 +210,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter { replayFile.close(); if (core.getSettingsRegistry().get(Setting.AUTO_POST_PROCESS)) { - MarkerProcessor.apply(outputPath); + MarkerProcessor.apply(outputPath, progress -> {}); } } catch (IOException e) { logger.error("Saving replay file:", e); diff --git a/src/main/java/com/replaymod/replay/ReplayHandler.java b/src/main/java/com/replaymod/replay/ReplayHandler.java index e240b52d..b3cb2fc7 100755 --- a/src/main/java/com/replaymod/replay/ReplayHandler.java +++ b/src/main/java/com/replaymod/replay/ReplayHandler.java @@ -112,7 +112,7 @@ public class ReplayHandler { */ private boolean suppressCameraMovements; - private final List markers; + private Set markers; private final GuiReplayOverlay overlay; @@ -135,7 +135,7 @@ public class ReplayHandler { FML_BUS.post(new ReplayOpenEvent.Pre(this)); - markers = new ArrayList<>(replayFile.getMarkers().or(Collections.emptySet())); + markers = replayFile.getMarkers().or(Collections.emptySet()); fullReplaySender = new FullReplaySender(this, replayFile, false); //#if MC>=10904 @@ -432,26 +432,6 @@ public class ReplayHandler { this.suppressCameraMovements = suppressCameraMovements; } - /** - * Returns all markers. - * When changed, {@link #saveMarkers()} should be called to save the changes. - * @return Collection of markers in no particular order - */ - public Collection getMarkers() { - return markers; - } - - /** - * Saves all markers. - */ - public void saveMarkers() { - try { - replayFile.writeMarkers(new HashSet<>(markers)); - } catch (IOException e) { - e.printStackTrace(); - } - } - /** * Spectate the specified entity. * When the entity is {@code null} or the camera entity, the camera becomes the view entity. diff --git a/src/main/java/com/replaymod/replay/ReplayModReplay.java b/src/main/java/com/replaymod/replay/ReplayModReplay.java index 077b8f20..4ece897f 100644 --- a/src/main/java/com/replaymod/replay/ReplayModReplay.java +++ b/src/main/java/com/replaymod/replay/ReplayModReplay.java @@ -14,7 +14,6 @@ import com.replaymod.replay.camera.CameraControllerRegistry; import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replay.camera.ClassicCameraController; import com.replaymod.replay.camera.VanillaCameraController; -import com.replaymod.replay.gui.overlay.GuiMarkerTimeline; import com.replaymod.replay.gui.screen.GuiModCompatWarning; import com.replaymod.replay.handler.GuiHandler; import com.replaymod.replaystudio.data.Marker; @@ -75,24 +74,12 @@ public class ReplayModReplay implements Module { marker.setYaw(camera.rotationYaw); marker.setPitch(camera.rotationPitch); marker.setRoll(camera.roll); - replayHandler.getMarkers().add(marker); - replayHandler.saveMarkers(); + replayHandler.getOverlay().timeline.addMarker(marker); } } } }); - registry.registerRaw(Keyboard.KEY_DELETE, () -> { - if (replayHandler != null) { - GuiMarkerTimeline timeline = replayHandler.getOverlay().timeline; - if (timeline.getSelectedMarker() != null) { - replayHandler.getMarkers().remove(timeline.getSelectedMarker()); - replayHandler.saveMarkers(); - timeline.setSelectedMarker(null); - } - } - }); - registry.registerKeyBinding("replaymod.input.thumbnail", Keyboard.KEY_N, new Runnable() { @Override public void run() { diff --git a/src/main/java/com/replaymod/replay/gui/overlay/GuiEditMarkerPopup.java b/src/main/java/com/replaymod/replay/gui/overlay/GuiEditMarkerPopup.java index 3d797cf6..4b2c9f04 100644 --- a/src/main/java/com/replaymod/replay/gui/overlay/GuiEditMarkerPopup.java +++ b/src/main/java/com/replaymod/replay/gui/overlay/GuiEditMarkerPopup.java @@ -16,6 +16,8 @@ import de.johni0702.minecraft.gui.utils.lwjgl.ReadablePoint; //#if MC>=11300 import com.replaymod.core.versions.MCVer.Keyboard; + +import java.util.function.Consumer; //#else //$$ import org.lwjgl.input.Keyboard; //#endif @@ -25,8 +27,7 @@ public class GuiEditMarkerPopup extends AbstractGuiPopup imp return new GuiNumberField().setSize(150, 20).setValidateOnFocusChange(true); } - private final ReplayHandler replayHandler; - private final Marker marker; + private final Consumer onSave; public final GuiLabel title = new GuiLabel().setI18nText("replaymod.gui.editkeyframe.title.marker"); @@ -65,6 +66,7 @@ public class GuiEditMarkerPopup extends AbstractGuiPopup imp public final GuiButton saveButton = new GuiButton().onClick(new Runnable() { @Override public void run() { + Marker marker = new Marker(); marker.setName(Strings.emptyToNull(nameField.getText())); marker.setTime(timeField.getInteger()); marker.setX(xField.getDouble()); @@ -73,7 +75,7 @@ public class GuiEditMarkerPopup extends AbstractGuiPopup imp marker.setYaw(yawField.getFloat()); marker.setPitch(pitchField.getFloat()); marker.setRoll(rollField.getFloat()); - replayHandler.saveMarkers(); + onSave.accept(marker); close(); } }).setSize(150, 20).setI18nLabel("replaymod.gui.save"); @@ -89,10 +91,9 @@ public class GuiEditMarkerPopup extends AbstractGuiPopup imp .setLayout(new HorizontalLayout(HorizontalLayout.Alignment.CENTER).setSpacing(7)) .addElements(new HorizontalLayout.Data(0.5), saveButton, cancelButton); - public GuiEditMarkerPopup(ReplayHandler replayHandler, GuiContainer container, Marker marker) { + public GuiEditMarkerPopup(GuiContainer container, Marker marker, Consumer onSave) { super(container); - this.replayHandler = replayHandler; - this.marker = marker; + this.onSave = onSave; setBackgroundColor(Colors.DARK_TRANSPARENT); diff --git a/src/main/java/com/replaymod/replay/gui/overlay/GuiMarkerTimeline.java b/src/main/java/com/replaymod/replay/gui/overlay/GuiMarkerTimeline.java index d48343c0..0cf96d05 100644 --- a/src/main/java/com/replaymod/replay/gui/overlay/GuiMarkerTimeline.java +++ b/src/main/java/com/replaymod/replay/gui/overlay/GuiMarkerTimeline.java @@ -2,27 +2,46 @@ package com.replaymod.replay.gui.overlay; import com.replaymod.core.ReplayMod; import com.replaymod.replay.ReplayHandler; +import com.replaymod.replay.ReplayModReplay; import com.replaymod.replaystudio.data.Marker; import com.replaymod.replaystudio.util.Location; import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.element.advanced.AbstractGuiTimeline; import de.johni0702.minecraft.gui.function.Draggable; +import de.johni0702.minecraft.gui.function.Typeable; import de.johni0702.minecraft.gui.utils.lwjgl.Point; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; import de.johni0702.minecraft.gui.utils.lwjgl.ReadablePoint; import net.minecraft.client.resources.I18n; +//#if MC>=11300 +import com.replaymod.core.versions.MCVer.Keyboard; +//#else +//$$ import org.lwjgl.input.Keyboard; +//#endif + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; + import static de.johni0702.minecraft.gui.utils.Utils.clamp; -public class GuiMarkerTimeline extends AbstractGuiTimeline implements Draggable { +public class GuiMarkerTimeline extends AbstractGuiTimeline implements Draggable, Typeable { protected static final int TEXTURE_MARKER_X = 109; protected static final int TEXTURE_MARKER_Y = 20; protected static final int TEXTURE_MARKER_SELECTED_X = 114; protected static final int TEXTURE_MARKER_SELECTED_Y = 20; protected static final int MARKER_SIZE = 5; + @Nullable private final ReplayHandler replayHandler; + private final Consumer> saveMarkers; + protected Set markers; private ReadableDimension lastSize; @@ -32,8 +51,27 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline im private boolean dragging; private long lastClickTime; - public GuiMarkerTimeline(ReplayHandler replayHandler) { + public GuiMarkerTimeline(@Nonnull ReplayHandler replayHandler) { this.replayHandler = replayHandler; + try { + this.markers = replayHandler.getReplayFile().getMarkers().or(HashSet::new); + } catch (IOException e) { + ReplayModReplay.LOGGER.error("Failed to get markers from replay", e); + this.markers = new HashSet<>(); + } + this.saveMarkers = (markers) -> { + try { + replayHandler.getReplayFile().writeMarkers(markers); + } catch (IOException e) { + ReplayModReplay.LOGGER.error("Failed to save markers to replay", e); + } + }; + } + + public GuiMarkerTimeline(Set markers, Consumer> saveMarkers) { + this.replayHandler = null; + this.markers = markers; + this.saveMarkers = saveMarkers; } @Override @@ -52,7 +90,7 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline im protected void drawMarkers(GuiRenderer renderer, ReadableDimension size) { renderer.bindTexture(ReplayMod.TEXTURE); - for (Marker marker : replayHandler.getMarkers()) { + for (Marker marker : markers) { drawMarker(renderer, size, marker); } } @@ -63,7 +101,10 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline im double positionInVisible = markerPos - getOffset(); double fractionOfVisible = positionInVisible / visibleLength; int markerX = (int) (BORDER_LEFT + fractionOfVisible * (size.getWidth() - BORDER_LEFT - BORDER_RIGHT)); + drawMarker(renderer, size, marker, markerX); + } + protected void drawMarker(GuiRenderer renderer, ReadableDimension size, Marker marker, int markerX) { int textureX, textureY; if (marker.equals(selectedMarker)) { textureX = TEXTURE_MARKER_SELECTED_X; @@ -99,7 +140,7 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline im int visibleLength = (int) (getLength() * getZoom()); int contentWidth = lastSize.getWidth() - BORDER_LEFT - BORDER_RIGHT; - for (Marker marker : replayHandler.getMarkers()) { + for (Marker marker : markers) { int markerPos = clamp(marker.getTime(), getOffset(), getOffset() + visibleLength); double positionInVisible = markerPos - getOffset(); double fractionOfVisible = positionInVisible / visibleLength; @@ -122,16 +163,24 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline im draggingStartX = position.getX(); draggingTimeDelta = marker.getTime() - getTimeAt(position.getX(), position.getY()); } else { // Double click - new GuiEditMarkerPopup(replayHandler, getContainer(), marker).open(); + new GuiEditMarkerPopup(getContainer(), marker, (updatedMarker) -> { + markers.remove(marker); + markers.add(updatedMarker); + saveMarkers.accept(markers); + }).open(); } lastClickTime = now; } else if (button == 1) { // Right click selectedMarker = null; - replayHandler.setTargetPosition(new Location( - marker.getX(), marker.getY(), marker.getZ(), - marker.getPitch(), marker.getYaw() - )); - replayHandler.doJump(marker.getTime(), false); + if (replayHandler != null) { + replayHandler.setTargetPosition(new Location( + marker.getX(), marker.getY(), marker.getZ(), + marker.getPitch(), marker.getYaw() + )); + replayHandler.doJump(marker.getTime(), false); + } else { + setCursorPosition(marker.getTime()); + } } return true; } else { @@ -164,7 +213,7 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline im mouseDrag(position, button, 0); if (dragging) { dragging = false; - replayHandler.saveMarkers(); + saveMarkers.accept(markers); return true; } } @@ -187,4 +236,19 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline im public Marker getSelectedMarker() { return selectedMarker; } + + @Override + public boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown) { + if (keyCode == Keyboard.KEY_DELETE && selectedMarker != null) { + markers.remove(selectedMarker); + saveMarkers.accept(markers); + return true; + } + return false; + } + + public void addMarker(Marker marker) { + markers.add(marker); + saveMarkers.accept(markers); + } } diff --git a/src/main/java/com/replaymod/replay/handler/GuiHandler.java b/src/main/java/com/replaymod/replay/handler/GuiHandler.java index 4dbbb260..1bc1bc28 100644 --- a/src/main/java/com/replaymod/replay/handler/GuiHandler.java +++ b/src/main/java/com/replaymod/replay/handler/GuiHandler.java @@ -153,7 +153,7 @@ public class GuiHandler { return; } GuiButton button = new GuiButton(BUTTON_REPLAY_VIEWER, getGui(event).width / 2 - 100, - getGui(event).height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayviewer")) { + getGui(event).height / 4 + 10 + 4 * 24, I18n.format("replaymod.gui.replayviewer")) { //#if MC>=11300 @Override public void onClick(double mouseX, double mouseY) { diff --git a/src/main/resources/assets/replaymod/lang b/src/main/resources/assets/replaymod/lang index 967405aa..fdce012f 160000 --- a/src/main/resources/assets/replaymod/lang +++ b/src/main/resources/assets/replaymod/lang @@ -1 +1 @@ -Subproject commit 967405aafad5e7613c3eb9760d8eab03ad4e1567 +Subproject commit fdce012f2cc2126596506e6d38c4d7f69166559f