Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import io.github.itzispyder.clickcrystals.scripting.syntax.macros.*;
import io.github.itzispyder.clickcrystals.scripting.syntax.macros.camera.SnapToCmd;
import io.github.itzispyder.clickcrystals.scripting.syntax.macros.camera.TurnToCmd;
import io.github.itzispyder.clickcrystals.scripting.syntax.macros.inventory.CraftCmd;
import io.github.itzispyder.clickcrystals.scripting.syntax.macros.inventory.GuiDropCmd;
import io.github.itzispyder.clickcrystals.scripting.syntax.macros.inventory.GuiQuickMoveCmd;
import io.github.itzispyder.clickcrystals.scripting.syntax.macros.inventory.GuiSwapCmd;
Expand Down Expand Up @@ -213,6 +214,7 @@ public void initClickScript() {
ClickScript.register(new GuiSwapCmd());
ClickScript.register(new GuiDropCmd());
ClickScript.register(new GuiQuickMoveCmd());
ClickScript.register(new CraftCmd());
ClickScript.register(new DamageCmd());
ClickScript.register(new InteractCmd());
ClickScript.register(new DefineCmd());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class Config implements JsonSerializable<Config>, Global {
private boolean overviewMode;
private boolean disableCustomLoading;
private boolean disableModuleToggleBroadcast;
private boolean disableScriptChatErrors;
private boolean modMenuIntegration;
private boolean devMode;
private int readAnnouncementCount;
Expand Down Expand Up @@ -222,6 +223,14 @@ public void setDisableModuleToggleBroadcast(boolean disableModuleToggleBroadcast
this.disableModuleToggleBroadcast = disableModuleToggleBroadcast;
}

public boolean isDisableScriptChatErrors() {
return disableScriptChatErrors;
}

public void setDisableScriptChatErrors(boolean disableScriptChatErrors) {
this.disableScriptChatErrors = disableScriptChatErrors;
}

public String getCustomPath() {
return customPath;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ public void setHeight(int height) {
this.height = height;
}

// Vertical space this element occupies in a parent's layout; may differ from the rendered height (e.g. while animating).
public int getLayoutHeight() {
return height;
}

public String getTooltip() {
return tooltip;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,80 @@
import io.github.itzispyder.clickcrystals.gui.elements.common.AbstractElement;
import io.github.itzispyder.clickcrystals.gui.misc.Shades;
import io.github.itzispyder.clickcrystals.gui.misc.Tex;
import io.github.itzispyder.clickcrystals.gui.misc.animators.Animations;
import io.github.itzispyder.clickcrystals.gui.misc.animators.Animator;
import io.github.itzispyder.clickcrystals.modules.ModuleSetting;
import io.github.itzispyder.clickcrystals.util.MathUtils;
import io.github.itzispyder.clickcrystals.util.minecraft.TextUtils;
import io.github.itzispyder.clickcrystals.util.minecraft.render.RenderUtils;
import net.minecraft.client.gui.GuiGraphicsExtractor;

public abstract class SettingElement<T extends ModuleSetting<?>> extends GuiElement {

private static final long VISIBILITY_ANIM_MS = 250;

protected final T setting;
protected boolean shouldUnderline;
private final int slotHeight;
private Animator visibilityAnimator;
private boolean lastVisible;

public SettingElement(T setting, int x, int y) {
super(x, y, 270, 40);
this.shouldUnderline = false;
this.setting = setting;
this.slotHeight = height;
this.lastVisible = setting.isVisible();
}

// 0 = fully hidden, 1 = fully shown; animates when the setting's visibility condition flips.
private double visibility() {
boolean visible = setting.isVisible();
if (visible != lastVisible) {
lastVisible = visible;
visibilityAnimator = new Animator(VISIBILITY_ANIM_MS, Animations.FADE_IN_AND_OUT);
visibilityAnimator.setReversed(!visible);
visibilityAnimator.reset();
}
if (visibilityAnimator == null)
return visible ? 1 : 0;
if (visibilityAnimator.isFinished()) {
visibilityAnimator = null;
return visible ? 1 : 0;
}
return MathUtils.clamp(visibilityAnimator.getAnimation(), 0.0, 1.0);
}

@Override
public int getLayoutHeight() {
return (int) (slotHeight * visibility());
}

@Override
public void render(GuiGraphicsExtractor context, int mouseX, int mouseY) {
double factor = visibility();
if (factor <= 0.01) {
setRendering(false);
return;
}
setRendering(true);

if (factor >= 0.99) {
super.render(context, mouseX, mouseY);
return;
}

// collapsing/expanding: clip to the animated slot height (matches the reserved layout height)
int clip = Math.max(1, (int) (slotHeight * factor));
context.enableScissor(x, y, x + width, y + clip);
super.render(context, mouseX, mouseY);
context.disableScissor();
}

@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (setting.isVisible())
super.mouseClicked(mouseX, mouseY, button);
}

public void renderSettingDetails(GuiGraphicsExtractor context) {
Expand Down Expand Up @@ -66,6 +126,6 @@ public boolean isHovered(int mouseX, int mouseY) {
int bxw = x + width - 5;
int by = y + height / 2 - 4;
int byh = y + height / 2 + 12;
return rendering && mouseX > bx && mouseX < bxw && mouseY > by && mouseY < byh;
return rendering && setting.isVisible() && mouseX > bx && mouseX < bxw && mouseY > by && mouseY < byh;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,20 @@ public SettingSectionElement(SettingSection settingSection, int x, int y) {
height = caret - y;
}

// Re-stacks settings each frame so hidden ones collapse and the rest slide to fill the gap.
private void layoutSettings() {
int caret = y + 30;
for (GuiElement child : getChildren()) {
if (child.y != caret)
child.move(0, caret - child.y);
caret += child.getLayoutHeight();
}
height = caret - y;
}

@Override
public void render(GuiGraphicsExtractor context, int mouseX, int mouseY) {
layoutSettings();
boolean isAnimating = animator != null && !animator.isFinished();
if (isAnimating) {
context.pose().pushMatrix();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class ScrollPanelElement extends GuiElement {
public static final int SCROLL_MULTIPLIER = 15;
private int remainingUp, remainingDown, limitTop, limitBottom, scrollbarY, scrollbarHeight, prevDrag;
private boolean scrolling;
private boolean verticalStack;
private int stackGap;

private final Animator interpolation;
private int interpolationLength;
Expand Down Expand Up @@ -95,13 +97,37 @@ public void recalculatePositions() {
}
}

// Stack children vertically by their layout height, so collapsed/animating elements close the gap.
public ScrollPanelElement verticalStack(int gap) {
this.verticalStack = true;
this.stackGap = gap;
return this;
}

// Anchors on the first child (which already tracks the scroll offset) and re-flows the rest below it.
private void restack() {
if (!verticalStack || getChildren().isEmpty())
return;

GuiElement first = getChildren().get(0);
int caret = first.y + first.getLayoutHeight() + stackGap;
for (int i = 1; i < getChildren().size(); i++) {
GuiElement child = getChildren().get(i);
if (child.y != caret)
child.move(0, caret - child.y);
caret += child.getLayoutHeight() + stackGap;
}
recalculatePositions();
}

@Override
public void onRender(GuiGraphicsExtractor context, int mouseX, int mouseY) {

}

@Override
public void render(GuiGraphicsExtractor context, int mouseX, int mouseY) {
restack();
boolean bl = canRender();

float interpolatedDelta = (float)(interpolationLength * interpolation.getProgressClampedReversed());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,51 @@ private int selMax() {
return Math.max(selectionStart, selectionEnd);
}

// Comments or uncomments every line touched by the cursor or selection (Ctrl+A then Ctrl+/ covers all lines).
private boolean toggleComment() {
int from = selectedAll ? 0 : selMin();
int to = selectedAll ? content.length() : selMax();
int blockStart = lineStart(from);
int blockEnd = lineEnd(to);
boolean collapsed = !selectedAll && !hasRange();

String block = content.substring(blockStart, blockEnd);
String[] lines = block.split("\n", -1);

// Uncomment only when every non-blank line is already commented; otherwise comment them all.
boolean allCommented = true;
for (String line : lines)
if (!line.isBlank() && !line.startsWith("//")) {
allCommented = false;
break;
}

StringBuilder rebuilt = new StringBuilder();
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
if (!line.isBlank())
line = allCommented ? line.substring(line.startsWith("// ") ? 3 : 2) : "// " + line;
rebuilt.append(line);
if (i < lines.length - 1) rebuilt.append("\n");
}

pushUndo();
int savedStart = selectionStart;
content = content.substring(0, blockStart) + rebuilt + content.substring(blockEnd);

if (collapsed) {
int delta = rebuilt.length() - block.length();
selectionStart = selectionEnd = MathUtils.clamp(savedStart + delta, blockStart, blockStart + rebuilt.length());
} else {
selectionStart = blockStart;
selectionEnd = blockStart + rebuilt.length();
}
styledContent = style(content);
updateSelection();
preferredCol = -1;
return true;
}

private void deleteRange() {
pushUndo();
int lo = selectedAll ? 0 : selMin();
Expand Down Expand Up @@ -270,28 +315,7 @@ private boolean handleKeyInner(int key, GuiScreen screen) {
return true;
}
case GLFW.GLFW_KEY_SLASH -> {
int ls = lineStart(selectionStart);
int le = lineEnd(selectionStart);
String line = content.substring(ls, le);
pushUndo();
String newLine;
int delta;
if (line.startsWith("// ")) {
newLine = line.substring(3);
delta = -3;
} else if (line.startsWith("//")) {
newLine = line.substring(2);
delta = -2;
} else {
newLine = "// " + line;
delta = 3;
}
content = content.substring(0, ls) + newLine + content.substring(le);
selectionStart = selectionEnd = MathUtils.clamp(selectionStart + delta, ls, ls + newLine.length());
styledContent = style(content);
updateSelection();
preferredCol = -1;
return true;
return toggleComment();
}
case GLFW.GLFW_KEY_LEFT -> {
selectionStart = selectionEnd = prevWordBoundary(selectionStart);
Expand Down Expand Up @@ -772,6 +796,8 @@ public void clearUndoHistory() {
public static class TextHighlighter {
private List<HighlightFactory> stringFactories = new ArrayList<>();
private ChatColor originalColor;
private String commentPrefix;
private ChatColor commentColor = ChatColor.GRAY;

public TextHighlighter(ChatColor originalColor) {
this.originalColor = originalColor;
Expand All @@ -781,10 +807,22 @@ public TextHighlighter() {
this(ChatColor.WHITE);
}

// Renders whole lines starting with the given prefix in a single comment color.
public TextHighlighter comments(String prefix, ChatColor color) {
this.commentPrefix = prefix;
this.commentColor = color;
return this;
}

public String highlightText(String text) {
String[] lines = text.lines().toArray(String[]::new);
StringBuilder result = new StringBuilder();
for (int i = 0; i < lines.length; i++) {
if (commentPrefix != null && lines[i].stripLeading().startsWith(commentPrefix)) {
result.append("%s%s%s".formatted(commentColor, lines[i], originalColor));
if (i < lines.length - 1) result.append("\n");
continue;
}
String[] words = lines[i].split(" ");
for (int j = 0; j < words.length; j++) {
String word = words[j];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public ModuleEditElement(GuiScreen parentScreen, Module module, int x, int y) {
this.setDraggable(true);
this.module = module;

ScrollPanelElement panel = new ScrollPanelElement(parentScreen, x + 5, y + 21, width - 5, height - 21);
ScrollPanelElement panel = new ScrollPanelElement(parentScreen, x + 5, y + 21, width - 5, height - 21).verticalStack(5);
int caret = y + 25;

for (SettingSection section : module.getData().getSettingSections()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public ModuleEditScreen(Module module) {
this.module = module;
previousOpened = module;

ScrollPanelElement panel = new ScrollPanelElement(this, contentX + 5, contentY + 21, contentWidth - 5, contentHeight - 21);
ScrollPanelElement panel = new ScrollPanelElement(this, contentX + 5, contentY + 21, contentWidth - 5, contentHeight - 21).verticalStack(5);
int caret = contentY + 25;

for (SettingSection section : module.getData().getSettingSections()) {
Expand Down
Loading