From 0c8cd80acdc75cb60fdc86bfaf11634b5b15b8ed Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 15:04:33 +0000 Subject: [PATCH 1/5] Show syntax error tooltips below their line, pointing at the error The tooltip of a syntax error was placed at the mouse, often covering the line it describes. Like in other editors, it is now placed below all rows of the line, or above them if there is no room, with an arrow pointing at the column of the error. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018MRyTdV3fSV5Q5NPGep4q9 --- .../gui/component/input/WidgetTextArea.java | 59 +++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java b/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java index dc63f2fbb..e019a7f74 100644 --- a/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java +++ b/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java @@ -28,6 +28,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.Style; +import net.minecraft.util.FormattedCharSequence; import net.minecraft.util.StringUtil; import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.OnlyIn; @@ -35,6 +36,7 @@ import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableInt; import org.apache.commons.lang3.tuple.Pair; +import org.joml.Vector2i; import org.cyclops.cyclopscore.client.gui.component.WidgetScrollBar; import org.cyclops.cyclopscore.client.gui.component.input.IInputListener; import org.cyclops.cyclopscore.helper.Helpers; @@ -72,6 +74,13 @@ public class WidgetTextArea extends EditBox implements GuiEventListener { public static final int ROW_HEIGHT = 9; private static final int COMPLETION_ROWS = 6; public static final int SCROLLBAR_WIDTH = 14; + // Height of the arrow between a syntax error tooltip and its error + private static final int TOOLTIP_ARROW = 4; + // How far vanilla tooltip backgrounds extend beyond their contents + private static final int TOOLTIP_BORDER = 4; + // Colors of vanilla tooltips + private static final int TOOLTIP_BACKGROUND = 0xF0100010; + private static final int TOOLTIP_BORDER_COLOR = 0x505000FF; private final TextFieldHelperExtended textFieldHelper; private final Font font; @@ -923,20 +932,62 @@ private void renderSyntaxError(GuiGraphics guiGraphics, DisplayCache displayCach int pos = Math.min(value.length(), TextEditing.lineOffset(value, this.syntaxError.line()) + Math.max(0, this.syntaxError.column() - 1)); int line = findLineFromPos(displayCache.lineStarts, pos); int row = line - this.firstRow; + int errorCenterX = -1; if (row >= 0 && row < this.getHeight() / ROW_HEIGHT) { int x = this.font.width(value.substring(displayCache.lineStarts[line], pos)); int width = pos < value.length() && value.charAt(pos) != '\n' ? this.font.width(value.substring(pos, pos + 1)) : 4; Pos2i screenPos = this.convertLocalToScreen(new Pos2i(x, row * ROW_HEIGHT)); guiGraphics.fill(screenPos.x, screenPos.y + ROW_HEIGHT - 2, screenPos.x + width, screenPos.y + ROW_HEIGHT - 1, Helpers.RGBAToInt(220, 0, 0, 255)); + errorCenterX = screenPos.x + width / 2; } - // Show the message when hovering the error line + // Show the message when hovering the error line, which may be wrapped over multiple rows + int top = Integer.MAX_VALUE; + int bottom = Integer.MIN_VALUE; + boolean hovering = false; for (LineInfo lineInfo : lines) { - if (lineInfo.lineNumber + 1 == this.syntaxError.line() && mouseX >= this.getX() && mouseX < this.getX() + this.getWidth() - && mouseY >= lineInfo.y && mouseY < lineInfo.y + ROW_HEIGHT && Minecraft.getInstance().screen != null) { - Minecraft.getInstance().screen.setTooltipForNextRenderPass(this.font.split(Component.literal(this.syntaxError.message()), 200)); + if (lineInfo.lineNumber + 1 == this.syntaxError.line()) { + top = Math.min(top, lineInfo.y); + bottom = Math.max(bottom, lineInfo.y + ROW_HEIGHT); + hovering |= mouseX >= this.getX() && mouseX < this.getX() + this.getWidth() + && mouseY >= lineInfo.y && mouseY < lineInfo.y + ROW_HEIGHT; } } + if (hovering && Minecraft.getInstance().screen != null) { + List message = this.font.split(Component.literal(this.syntaxError.message()), 200); + int screenHeight = Minecraft.getInstance().getWindow().getGuiScaledHeight(); + // Same height as vanilla tooltips of text lines + int tooltipHeight = message.size() * 10 - (message.size() == 1 ? 2 : 0); + + // Like in other editors, show the message below the line, or above it if there is no room + boolean below = bottom + TOOLTIP_ARROW + TOOLTIP_BORDER + tooltipHeight + TOOLTIP_BORDER <= screenHeight + || top - TOOLTIP_ARROW - TOOLTIP_BORDER - tooltipHeight - TOOLTIP_BORDER < 0; + int tooltipY = below ? bottom + TOOLTIP_ARROW + TOOLTIP_BORDER : top - TOOLTIP_ARROW - TOOLTIP_BORDER - tooltipHeight; + int anchorX = errorCenterX >= 0 ? errorCenterX - 12 : mouseX + 12; + Minecraft.getInstance().screen.setTooltipForNextRenderPass(message, (screenWidth, screenHeightIgnored, mouseXIgnored, mouseYIgnored, width, height) -> + new Vector2i(Math.max(TOOLTIP_BORDER, Math.min(anchorX, screenWidth - width - TOOLTIP_BORDER)), tooltipY), true); + + // Point at the column of the error + if (errorCenterX >= 0) { + this.renderTooltipArrow(guiGraphics, errorCenterX, below ? bottom : top - 1, below); + } + } + } + + /** + * Draw an arrow between a tooltip and the given position, in the style of vanilla tooltips. + */ + private void renderTooltipArrow(GuiGraphics guiGraphics, int x, int tipY, boolean pointUp) { + guiGraphics.pose().pushPose(); + // Same depth as tooltips + guiGraphics.pose().translate(0, 0, 400); + for (int i = 0; i < TOOLTIP_ARROW; i++) { + int y = pointUp ? tipY + i : tipY - i; + guiGraphics.fill(x - i, y, x + i + 1, y + 1, TOOLTIP_BACKGROUND); + guiGraphics.fill(x - i, y, x - i + 1, y + 1, TOOLTIP_BORDER_COLOR); + guiGraphics.fill(x + i, y, x + i + 1, y + 1, TOOLTIP_BORDER_COLOR); + } + guiGraphics.pose().popPose(); } private void renderCharBackground(GuiGraphics guiGraphics, DisplayCache displayCache, int pos, int color) { From 9508b99ef00c31b3c10c811cdcf5e733fb6e335f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 15:06:20 +0000 Subject: [PATCH 2/5] Make the script editor scrollbar narrower The scrollbar took as much width as an inventory scrollbar, leaving less room for text in the small editor. It is now about half as wide. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018MRyTdV3fSV5Q5NPGep4q9 --- .../client/gui/component/input/WidgetTextArea.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java b/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java index e019a7f74..3b263056e 100644 --- a/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java +++ b/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java @@ -73,7 +73,9 @@ public class WidgetTextArea extends EditBox implements GuiEventListener { public static final int ROW_HEIGHT = 9; private static final int COMPLETION_ROWS = 6; - public static final int SCROLLBAR_WIDTH = 14; + public static final int SCROLLBAR_WIDTH = 7; + // Same height as the vanilla scroller, which the scrollbar's drag handling assumes + private static final int SCROLLBAR_HANDLE_HEIGHT = 15; // Height of the arrow between a syntax error tooltip and its error private static final int TOOLTIP_ARROW = 4; // How far vanilla tooltip backgrounds extend beyond their contents @@ -156,9 +158,15 @@ public int getTotalRows() { @Override public void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { - // Only show scrollbar if needed + // Only show scrollbar if needed, and draw a narrower handle than the vanilla scroller to leave more room for text if (this.needsScrollBars()) { - super.renderWidget(guiGraphics, mouseX, mouseY, partialTicks); + int x = this.getX(); + int y = this.getY() + (int) ((this.getHeight() - SCROLLBAR_HANDLE_HEIGHT - 2) * this.getCurrentScroll()); + int x2 = x + SCROLLBAR_WIDTH - 1; + int y2 = y + SCROLLBAR_HANDLE_HEIGHT; + guiGraphics.fill(x, y, x2, y2, Helpers.RGBAToInt(85, 85, 85, 255)); + guiGraphics.fill(x, y, x2 - 1, y2 - 1, Helpers.RGBAToInt(255, 255, 255, 255)); + guiGraphics.fill(x + 1, y + 1, x2 - 1, y2 - 1, Helpers.RGBAToInt(198, 198, 198, 255)); } } }; From 59bbc1e222dde04249ba75c076bf6e0b948f4249 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 15:06:20 +0000 Subject: [PATCH 3/5] Show syntax errors when hovering their scrollbar mark The red mark of a syntax error on the scrollbar was drawn over the scrollbar handle, and gave no information when hovered. It is now drawn below the handle, and hovering it shows the error message. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018MRyTdV3fSV5Q5NPGep4q9 --- .../gui/component/input/WidgetTextArea.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java b/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java index 3b263056e..7ce778739 100644 --- a/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java +++ b/src/main/java/org/cyclops/integratedscripting/client/gui/component/input/WidgetTextArea.java @@ -922,15 +922,21 @@ public void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float // Render scrollbar if (this.scrollBar != null) { - this.scrollBar.render(guiGraphics, getX(), getY(), partialTicks); - - // Mark the position of the syntax error on the scrollbar, so it can be found when scrolled out of view + // Mark the position of the syntax error on the scrollbar, so it can be found when scrolled out of view. + // This is drawn below the handle, which only covers it when the error is roughly in view. if (this.syntaxError != null && displayCache.linesTotal > this.scrollBar.getVisibleRows()) { int errorRow = findLineFromPos(displayCache.lineStarts, TextEditing.lineOffset(this.getValue(), this.syntaxError.line())); int y = this.getY() + (int) ((long) errorRow * (this.getHeight() - 2) / displayCache.linesTotal); - int x = this.getX() + this.getWidth() - SCROLLBAR_WIDTH + 1; - guiGraphics.fill(x, y, x + SCROLLBAR_WIDTH - 2, y + 2, Helpers.RGBAToInt(220, 0, 0, 255)); + int x = this.getX() + this.getWidth() - SCROLLBAR_WIDTH; + guiGraphics.fill(x, y, x + SCROLLBAR_WIDTH - 1, y + 2, Helpers.RGBAToInt(220, 0, 0, 255)); + + // Show the message when hovering the mark + if (mouseX >= x && mouseX < x + SCROLLBAR_WIDTH - 1 && mouseY >= y - 1 && mouseY < y + 3 && Minecraft.getInstance().screen != null) { + Minecraft.getInstance().screen.setTooltipForNextRenderPass(this.font.split(Component.literal(this.syntaxError.message()), 200)); + } } + + this.scrollBar.render(guiGraphics, getX(), getY(), partialTicks); } } From accc4fefdf5eb10369299dfb21a43d81cbff8315 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 15:10:00 +0000 Subject: [PATCH 4/5] Hide JEI in the focus mode of the scripting terminal JEI drew its item list over the enlarged script editor. When JEI is installed, the focus mode now claims the whole screen, so JEI hides it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018MRyTdV3fSV5Q5NPGep4q9 --- build.gradle | 7 ++++ gradle.properties | 1 + .../ContainerScreenTerminalScripting.java | 4 ++ .../jei/JEIIntegratedScriptingPlugin.java | 38 +++++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 src/main/java/org/cyclops/integratedscripting/modcompat/jei/JEIIntegratedScriptingPlugin.java diff --git a/build.gradle b/build.gradle index 462b29cfd..6543bbef6 100644 --- a/build.gradle +++ b/build.gradle @@ -101,6 +101,10 @@ test { repositories { mavenCentral() mavenLocal() + maven { + name "JEI" + url "https://maven.blamejared.com" + } maven { name "Cyclops Repo" url "https://maven.pkg.github.com/CyclopsMC/packages" @@ -136,6 +140,9 @@ dependencies { } } + // Optional, only used to hide JEI in focus mode + compileOnly "mezz.jei:jei-${project.minecraft_version}-neoforge-api:${project.jei_version}" + modLib "com.google.re2j:re2j:1.1" modLib ("io.netty:netty-codec-http:4.1.77.Final") { transitive = false diff --git a/gradle.properties b/gradle.properties index 50806d5ce..ff9723dfd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -34,4 +34,5 @@ org.gradle.caching=true graal_version=25.2.4 cyclopscore_version=1.25.5-643 integrateddynamics_version=1.24.1-1004 +jei_version=19.16.0.152 commoncapabilities_version=2.9.3-147 diff --git a/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java b/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java index 0f126149a..27a2bca32 100644 --- a/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java +++ b/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java @@ -741,6 +741,10 @@ public void setFirstRow(int firstRow) { this.firstRow = firstRow; } + public boolean isFocusMode() { + return focusMode; + } + @Nullable private ILanguageHandler getActiveLanguageHandler() { Path path = getMenu().getActiveScriptPath(); diff --git a/src/main/java/org/cyclops/integratedscripting/modcompat/jei/JEIIntegratedScriptingPlugin.java b/src/main/java/org/cyclops/integratedscripting/modcompat/jei/JEIIntegratedScriptingPlugin.java new file mode 100644 index 000000000..9a9ab7774 --- /dev/null +++ b/src/main/java/org/cyclops/integratedscripting/modcompat/jei/JEIIntegratedScriptingPlugin.java @@ -0,0 +1,38 @@ +package org.cyclops.integratedscripting.modcompat.jei; + +import mezz.jei.api.IModPlugin; +import mezz.jei.api.JeiPlugin; +import mezz.jei.api.gui.handlers.IGuiContainerHandler; +import mezz.jei.api.registration.IGuiHandlerRegistration; +import net.minecraft.client.renderer.Rect2i; +import net.minecraft.resources.ResourceLocation; +import org.cyclops.integratedscripting.Reference; +import org.cyclops.integratedscripting.client.gui.container.ContainerScreenTerminalScripting; + +import java.util.Collections; +import java.util.List; + +/** + * Only loaded by JEI if it is installed. + * @author rubensworks + */ +@JeiPlugin +public class JEIIntegratedScriptingPlugin implements IModPlugin { + + @Override + public ResourceLocation getPluginUid() { + return ResourceLocation.fromNamespaceAndPath(Reference.MOD_ID, "main"); + } + + @Override + public void registerGuiHandlers(IGuiHandlerRegistration registration) { + registration.addGuiContainerHandler(ContainerScreenTerminalScripting.class, new IGuiContainerHandler<>() { + @Override + public List getGuiExtraAreas(ContainerScreenTerminalScripting screen) { + // Claim the whole screen in focus mode, so JEI hides its overlays instead of drawing them over the editor + return screen.isFocusMode() ? List.of(new Rect2i(0, 0, screen.width, screen.height)) : Collections.emptyList(); + } + }); + } + +} From 0a2a0f239780b4677bf77305651697a7f6b0e126 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 15:49:09 +0000 Subject: [PATCH 5/5] Keep JEI's buttons clear of the focus mode editor JEI keeps its bookmark and config buttons in the bottom corners of the screen, which overlapped the enlarged script editor. The focus mode now leaves a larger margin when JEI is installed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018MRyTdV3fSV5Q5NPGep4q9 --- .../ContainerScreenTerminalScripting.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java b/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java index 27a2bca32..8dd09fa51 100644 --- a/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java +++ b/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java @@ -24,6 +24,7 @@ import net.minecraft.util.StringUtil; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; +import net.neoforged.fml.ModList; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.Level; import org.cyclops.cyclopscore.client.gui.component.WidgetScrollBar; @@ -87,6 +88,8 @@ public class ContainerScreenTerminalScripting extends ContainerScreenExtended