Skip to content

Instantly share code, notes, and snippets.

@Densamisten
Created September 11, 2024 11:05
Show Gist options
  • Save Densamisten/5119199a2f068e5a27067d1fd20bc29a to your computer and use it in GitHub Desktop.
Save Densamisten/5119199a2f068e5a27067d1fd20bc29a to your computer and use it in GitHub Desktop.
package exonihility.client.gui;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonSyntaxException;
import exonihility.client.util.toasts.impl.builder.BasicToastBuilder;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.EditBoxWidget;
import net.minecraft.client.toast.SystemToast;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
import org.unix4j.Unix4j;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Stack;
public class AllyshipEditor extends Screen {
private EditBoxWidget editBoxWidget;
private String filePath;
private static final int MAX_UNDO_STACK_SIZE = 100;
private Stack<String> undoStack = new Stack<>(); // Stack to hold previous text states
private Stack<String> redoStack = new Stack<>(); // Stack to hold redo states
private Gson gson = new GsonBuilder().setPrettyPrinting().create(); // Gson with pretty-print enabled
public AllyshipEditor(String filePath) {
super(Text.literal("In-Game Text Editor"));
this.filePath = filePath;
}
@Override
public void init() {
super.init();
// Create a larger EditBoxWidget for text input
int editorWidth = this.width - 40;
int editorHeight = this.height - 100;
editBoxWidget = new EditBoxWidget(this.textRenderer, 20, 20, editorWidth, editorHeight, Text.literal("Type here..."), Text.literal("..."));
editBoxWidget.setFocused(true);
// Load file contents into the editor
String fileContents = readFileWithUnix4j(filePath);
editBoxWidget.setText(fileContents != null ? fileContents : "Failed to load file");
this.addSelectableChild(editBoxWidget);
// Notify all plugins that the editor is being opened
AllyshipEditorPluginRegistry.getPlugins().forEach(plugin -> plugin.onEditorOpen(this));
// Notify all plugins to add custom UI elements
AllyshipEditorPluginRegistry.getPlugins().forEach(plugin -> plugin.addCustomUI(this));
// Push initial state to undo stack
pushUndoState(editBoxWidget.getText());
// Save button with JSON validation check
ButtonWidget saveButton = ButtonWidget.builder(Text.literal("Save Changes"), button -> {
String modifiedText = editBoxWidget.getText();
for (AllyshipEditorPlugin plugin : AllyshipEditorPluginRegistry.getPlugins()) {
modifiedText = plugin.onSave(modifiedText);
}
saveFile(modifiedText); // Save file when button is clicked
})
.dimensions(20, this.height - 40, 100, 20)
.tooltip(Tooltip.of(Text.literal("Saves changes to file")))
.build();
addDrawableChild(saveButton);
}
// Detect Ctrl+Y for redo
private boolean isRedoKeyPressed() {
boolean ctrlPressed = GLFW.glfwGetKey(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_KEY_LEFT_CONTROL) == GLFW.GLFW_PRESS ||
GLFW.glfwGetKey(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_KEY_RIGHT_CONTROL) == GLFW.GLFW_PRESS;
boolean yPressed = GLFW.glfwGetKey(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_KEY_Y) == GLFW.GLFW_PRESS;
return ctrlPressed && yPressed;
}
// Redo functionality
private void redoText() {
if (!redoStack.isEmpty()) {
String redoState = redoStack.pop();
undoStack.push(editBoxWidget.getText());
editBoxWidget.setText(redoState);
}
}
// Undo functionality
private void undoText() {
if (!undoStack.isEmpty()) {
redoStack.push(editBoxWidget.getText());
undoStack.pop();
if (!undoStack.isEmpty()) {
String previousState = undoStack.peek();
editBoxWidget.setText(previousState);
} else {
editBoxWidget.setText("");
}
}
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
this.renderBackground(context, mouseX, mouseY, delta);
super.render(context, mouseX, mouseY, delta);
if (editBoxWidget != null) {
editBoxWidget.render(context, mouseX, mouseY, delta);
}
// Call the onRender method for all registered plugins
AllyshipEditorPluginRegistry.getPlugins().forEach(plugin -> plugin.onRender(this, context));
}
@Override
public void tick() {
super.tick();
// Handle undo/redo actions
if (isUndoKeyPressed()) {
undoText();
}
if (isRedoKeyPressed()) {
redoText();
}
// Beautify JSON content only if it's valid
try {
String currentText = editBoxWidget.getText();
if (isValidJson(currentText)) {
String beautifiedJson = gson.toJson(gson.fromJson(currentText, Object.class));
editBoxWidget.setText(beautifiedJson);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean isValidJson(String json) {
try {
gson.fromJson(json, JsonElement.class); // Parse JSON
return true;
} catch (JsonSyntaxException e) {
return false;
}
}
// Show a toast notification when invalid JSON is detected
private void showInvalidJsonToast() {
new BasicToastBuilder()
.title("Malformed JSON Error")
.description("Invalid JSON!") // Show a fixed error message
.build()
.show();
}
public String getEditBoxText() {
return editBoxWidget.getText();
}
@Override
public boolean shouldPause() {
return false;
}
// Function to read the file contents using Unix4J cat
private String readFileWithUnix4j(String path) {
try {
File file = new File(path);
return Unix4j.cat(file).toStringResult();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// Save the file only if JSON is valid
private void saveFile(String content) {
if (isValidJson(content)) {
try (FileWriter writer = new FileWriter(filePath)) {
writer.write(content); // Save the content
} catch (IOException e) {
e.printStackTrace();
}
} else {
showInvalidJsonToast(); // Show toast when JSON is malformed
}
}
private boolean undoKeyHeld = false;
// Detect Ctrl+Z for undo
private boolean isUndoKeyPressed() {
boolean ctrlPressed = GLFW.glfwGetKey(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_KEY_LEFT_CONTROL) == GLFW.GLFW_PRESS ||
GLFW.glfwGetKey(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_KEY_RIGHT_CONTROL) == GLFW.GLFW_PRESS;
boolean zPressed = GLFW.glfwGetKey(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_KEY_Z) == GLFW.GLFW_PRESS;
if (ctrlPressed && zPressed && !undoKeyHeld) {
undoKeyHeld = true;
return true;
}
if (!ctrlPressed || !zPressed) {
undoKeyHeld = false;
}
return false;
}
private void pushUndoState(String newState) {
if (undoStack.size() >= MAX_UNDO_STACK_SIZE) {
undoStack.remove(0); // Remove the oldest state to prevent overflow
}
undoStack.push(newState);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment