Re-work Replay Editor UI

This commit is contained in:
Jonas Herzig
2019-03-12 19:44:09 +01:00
parent eb6e7b79d0
commit 51c073012e
13 changed files with 396 additions and 98 deletions

View File

@@ -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

View File

@@ -23,7 +23,7 @@ public class ReplayModEditor implements Module {
@Override
public void initClient() {
new GuiHandler(this).register();
new GuiHandler().register();
}
public ReplayMod getCore() {

View File

@@ -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<GuiEditReplay> {
private final Path inputPath;
private final EditTimeline timeline;
private final GuiTimelineTime<GuiMarkerTimeline> 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<Marker> 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<GuiPanel>() {
@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<ProgressPopup> {
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<Marker> markers, Consumer<Set<Marker>> 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);
}
}
}
}

View File

@@ -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<Float> progress) throws IOException {
if (!hasWork(path)) {
return;
}
@@ -71,6 +72,7 @@ public class MarkerProcessor {
Iterator<Marker> 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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -112,7 +112,7 @@ public class ReplayHandler {
*/
private boolean suppressCameraMovements;
private final List<Marker> markers;
private Set<Marker> 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<Marker> 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.

View File

@@ -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() {

View File

@@ -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<GuiEditMarkerPopup> imp
return new GuiNumberField().setSize(150, 20).setValidateOnFocusChange(true);
}
private final ReplayHandler replayHandler;
private final Marker marker;
private final Consumer<Marker> onSave;
public final GuiLabel title = new GuiLabel().setI18nText("replaymod.gui.editkeyframe.title.marker");
@@ -65,6 +66,7 @@ public class GuiEditMarkerPopup extends AbstractGuiPopup<GuiEditMarkerPopup> 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<GuiEditMarkerPopup> 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<GuiEditMarkerPopup> 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<Marker> onSave) {
super(container);
this.replayHandler = replayHandler;
this.marker = marker;
this.onSave = onSave;
setBackgroundColor(Colors.DARK_TRANSPARENT);

View File

@@ -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<GuiMarkerTimeline> implements Draggable {
public class GuiMarkerTimeline extends AbstractGuiTimeline<GuiMarkerTimeline> 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<Set<Marker>> saveMarkers;
protected Set<Marker> markers;
private ReadableDimension lastSize;
@@ -32,8 +51,27 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline<GuiMarkerTimeline> 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<Marker> markers, Consumer<Set<Marker>> saveMarkers) {
this.replayHandler = null;
this.markers = markers;
this.saveMarkers = saveMarkers;
}
@Override
@@ -52,7 +90,7 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline<GuiMarkerTimeline> 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<GuiMarkerTimeline> 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<GuiMarkerTimeline> 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<GuiMarkerTimeline> 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<GuiMarkerTimeline> 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<GuiMarkerTimeline> 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);
}
}

View File

@@ -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) {