Re-work Replay Editor UI
This commit is contained in:
@@ -23,7 +23,7 @@ public class ReplayModEditor implements Module {
|
||||
|
||||
@Override
|
||||
public void initClient() {
|
||||
new GuiHandler(this).register();
|
||||
new GuiHandler().register();
|
||||
}
|
||||
|
||||
public ReplayMod getCore() {
|
||||
|
||||
262
src/main/java/com/replaymod/editor/gui/GuiEditReplay.java
Normal file
262
src/main/java/com/replaymod/editor/gui/GuiEditReplay.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user