GuiScriptInterface is now pannable! Core functionality added to GuiNPCInterface, which means technically any child of that can be pannable

This commit is contained in:
bigguy345 committed 2026-03-09 09:13:29 +02:00
1 parent 45cda3cc90
commit 18c15b13bc
2 files changed
+189 -29

No files matched your search

@@ -102,6 +102,7 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
this.drawDefaultBackground = true;
this.closeOnEsc = true;
this.xSize = 420;
this.isPannableGUI = true;
this.setBackground("menubg.png");
}
@@ -142,6 +143,7 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
public void initGui() {
// ==================== BASE LAYOUT CALCULATION ====================
isPanning = false;
this.ySize = (int) ((double) this.xSize * 0.56D);
if ((double) this.ySize > (double) this.height * 0.95D) {
this.ySize = (int) ((double) this.height * 0.95D);
@@ -334,6 +336,8 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
}
this.addScroll(scroll);
}
computePanBounds(editorX, editorY, editorWidth, editorHeight);
}
/**
* Initialize the settings tab layout (console view).
@@ -376,6 +380,12 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
// Language button requires options to be available
this.getButton(103).enabled = languageOptions.size() > 0;
}
int consoleX = this.guiLeft + 4 + yoffset;
int consoleY = this.guiTop + 6 + yoffset;
int consoleW = this.xSize - 160 - yoffset;
int consoleH = (int) ((float) this.ySize * 0.92F) - yoffset * 2;
computePanBounds(consoleX, consoleY, consoleW, consoleH);
}
public GuiScriptInterface setDimensions(int x, int y) {
@@ -393,21 +403,35 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
protected ScriptContext getScriptContext() {
return handler.getContext();
}
// ==================== MOUSE HANDLING ====================
@Override
public void mouseClicked(int mouseX, int mouseY, int mouseButton) {
// Check if click is within autocomplete menu bounds and consume it if so
protected boolean isPannableArea(int mx, int my) {
if (activeTab > 0 && fullscreenButton.isMouseOver(mx, my)) {
return false;
}
GuiScriptTextArea activeArea = getActiveScriptArea();
if (activeArea != null && activeArea.isPointOnAutocompleteMenu(mx, my)) {
return false;
}
return super.isPannableArea(mx, my);
}
@Override
public void mouseClicked(int i, int j, int k) {
int adjX = panAdjustedX(i);
int adjY = panAdjustedY(j);
GuiScriptTextArea activeArea = getActiveScriptArea();
boolean isOverAutocomplete = activeArea != null
&& activeArea.isPointOnAutocompleteMenu(mouseX, mouseY);
&& activeArea.isPointOnAutocompleteMenu(adjX, adjY);
if (isOverAutocomplete) {
activeArea.mouseClicked(mouseX, mouseY, mouseButton);
activeArea.mouseClicked(adjX, adjY, k);
return;
}
super.mouseClicked(mouseX, mouseY, mouseButton);
super.mouseClicked(i, j, k);
}
public String previousHookClicked = "";
@@ -61,6 +61,29 @@ public abstract class GuiNPCInterface extends GuiScreen {
public float bgScaleZ = 1;
public int bgTextureHeight = 256; // Actual content height in texture (for bottom border stitching)
// ==================== VIEWPORT PANNING ====================
/**
* If true, this GUI supports viewport panning via click-drag on empty areas.
* Subclasses set this to true and call computePanBounds() to enable.
*/
protected boolean isPannableGUI = false;
protected boolean isPanning = false;
protected double panStartX, panStartY;
/**
* Cumulative pan offset in scaled GUI pixels.
* Positive values = viewport shifted right/down (content appears to move left/up).
*/
public double currentPanX, currentPanY;
public static double activePanX, activePanY;
protected double minPanX, maxPanX, minPanY, maxPanY;
protected int panViewportX, panViewportY, panViewportWidth, panViewportHeight;
/** Pan-adjusted mouse coordinates, updated every frame in drawScreen for hit-testing panned components. */
protected int panAdjMouseX, panAdjMouseY;
public GuiNPCInterface(EntityNPCInterface npc) {
this.player = Minecraft.getMinecraft().thePlayer;
this.npc = npc;
@@ -161,29 +184,38 @@ public abstract class GuiNPCInterface extends GuiScreen {
if (subgui != null)
subgui.mouseClicked(i, j, k);
else {
mouseEvent(i, j, k);
vanillaMouseClicked(i, j, k);
int adjX = isPannableGUI ? panAdjustedX(i) : i;
int adjY = isPannableGUI ? panAdjustedY(j) : j;
if (isPannableGUI && k == 0 && isPannableArea(adjX, adjY)) {
isPanning = true;
panStartX = i;
panStartY = j;
return;
}
mouseEvent(adjX, adjY, k);
vanillaMouseClicked(adjX, adjY, k);
for (GuiScrollWindow guiScrollableComponent : scrollWindows.values()) {
guiScrollableComponent.mouseClicked(i, j, k);
guiScrollableComponent.mouseClicked(adjX, adjY, k);
}
if (k == 0) {
for (GuiCustomScroll scroll : new ArrayList<GuiCustomScroll>(scrolls.values())) {
scroll.mouseClicked(i, j, k);
scroll.mouseClicked(adjX, adjY, k);
}
}
// Process diagram mouse clicks
for (GuiDiagram diagram : diagrams.values()) {
if (diagram.isWithin(i, j)) {
if (diagram.mouseClicked(i, j, k))
if (diagram.isWithin(adjX, adjY)) {
if (diagram.mouseClicked(adjX, adjY, k))
return;
}
}
for (GuiNpcTextField tf : new ArrayList<GuiNpcTextField>(textfields.values()))
if (tf.enabled)
tf.mouseClicked(i, j, k);
tf.mouseClicked(adjX, adjY, k);
}
}
@@ -225,7 +257,12 @@ public abstract class GuiNPCInterface extends GuiScreen {
subgui.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick);
return;
}
super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick);
if (isPannableGUI && isPanning) {
return;
}
int adjX = isPannableGUI ? panAdjustedX(mouseX) : mouseX;
int adjY = isPannableGUI ? panAdjustedY(mouseY) : mouseY;
super.mouseClickMove(adjX, adjY, clickedMouseButton, timeSinceLastClick);
}
@Override
@@ -234,12 +271,72 @@ public abstract class GuiNPCInterface extends GuiScreen {
subgui.mouseMovedOrUp(mouseX, mouseY, state);
return;
}
super.mouseMovedOrUp(mouseX, mouseY, state);
if (isPannableGUI && state == 0 && isPanning) {
isPanning = false;
panStartX = 0;
panStartY = 0;
return;
}
int adjX = isPannableGUI ? panAdjustedX(mouseX) : mouseX;
int adjY = isPannableGUI ? panAdjustedY(mouseY) : mouseY;
super.mouseMovedOrUp(adjX, adjY, state);
}
public void mouseEvent(int i, int j, int k) {
}
// ==================== VIEWPORT PANNING METHODS ====================
protected boolean isPannableArea(int mx, int my) {
for (Object obj : this.buttonList) {
GuiButton btn = (GuiButton) obj;
if (btn.visible && mx >= btn.xPosition && mx < btn.xPosition + btn.width
&& my >= btn.yPosition && my < btn.yPosition + btn.height) {
return false;
}
}
for (GuiCustomScroll scroll : scrolls.values()) {
if (scroll.visible && scroll.isMouseOver(mx, my)) {
return false;
}
}
for (GuiNpcTextField tf : textfields.values()) {
if (tf.enabled && mx >= tf.xPosition && mx < tf.xPosition + tf.width
&& my >= tf.yPosition && my < tf.yPosition + tf.height) {
return false;
}
}
return true;
}
protected void computePanBounds(int vpX, int vpY, int vpW, int vpH) {
this.panViewportX = vpX;
this.panViewportY = vpY;
this.panViewportWidth = vpW;
this.panViewportHeight = vpH;
int panRange = Math.max(this.width, this.height) / 2;
this.minPanX = -panRange;
this.maxPanX = panRange;
this.minPanY = -panRange;
this.maxPanY = panRange;
}
protected int panAdjustedX(int rawMouseX) {
return rawMouseX + (int) currentPanX;
}
protected int panAdjustedY(int rawMouseY) {
return rawMouseY + (int) currentPanY;
}
public boolean hasPanOffset() {
return isPannableGUI && (currentPanX != 0 || currentPanY != 0);
}
@Override
protected void actionPerformed(GuiButton guibutton) {
if (subgui != null)
@@ -377,9 +474,43 @@ public abstract class GuiNPCInterface extends GuiScreen {
public void drawScreen(int i, int j, float f) {
mouseX = i;
mouseY = j;
boolean panning = hasPanOffset();
int drawX = panning ? panAdjustedX(i) : i;
int drawY = panning ? panAdjustedY(j) : j;
panAdjMouseX = drawX;
panAdjMouseY = drawY;
// Smooth pan: update pan offset every frame using raw mouse delta
if (isPannableGUI && isPanning && Mouse.isButtonDown(0)) {
double deltaX = i - panStartX;
double deltaY = j - panStartY;
currentPanX = Math.max(minPanX, Math.min(maxPanX, currentPanX - deltaX));
currentPanY = Math.max(minPanY, Math.min(maxPanY, currentPanY - deltaY));
panStartX = i;
panStartY = j;
panning = hasPanOffset();
drawX = panning ? panAdjustedX(i) : i;
drawY = panning ? panAdjustedY(j) : j;
panAdjMouseX = drawX;
panAdjMouseY = drawY;
} else if (isPanning && !Mouse.isButtonDown(0)) {
isPanning = false;
}
if (drawDefaultBackground && subgui == null)
drawDefaultBackground();
if (panning) {
GL11.glPushMatrix();
GL11.glTranslated(-currentPanX, -currentPanY, 0);
activePanX = currentPanX;
activePanY = currentPanY;
}
if (background != null && mc.renderEngine != null) {
drawBackground();
}
@@ -390,40 +521,45 @@ public abstract class GuiNPCInterface extends GuiScreen {
label.drawLabel(this, fontRendererObj);
for (GuiCustomScroll scroll : scrolls.values()) {
scroll.updateSubGUI(subGui);
scroll.drawScreen(i, j, f, !subGui && scroll.isMouseOver(i, j) ? Mouse.getDWheel() : 0);
scroll.drawScreen(drawX, drawY, f, !subGui && scroll.isMouseOver(drawX, drawY) ? Mouse.getDWheel() : 0);
}
// Draw scrollable windows.
for (GuiScrollWindow guiScrollableComponent : scrollWindows.values()) {
guiScrollableComponent.drawScreen(i, j, f, !subGui && guiScrollableComponent.isMouseOver(i, j) ? Mouse.getDWheel() : 0);
guiScrollableComponent.drawScreen(drawX, drawY, f, !subGui && guiScrollableComponent.isMouseOver(drawX, drawY) ? Mouse.getDWheel() : 0);
}
for (GuiDiagram diagram : diagrams.values()) {
diagram.drawDiagram(i, j, subGui);
diagram.drawDiagram(drawX, drawY, subGui);
}
super.drawScreen(i, j, f);
super.drawScreen(drawX, drawY, f);
for (GuiCustomScroll scroll : scrolls.values())
if (scroll.hoverableText) {
scroll.drawHover(i, j);
scroll.drawHover(drawX, drawY);
}
for (GuiNpcButton button : buttons.values()) {
button.updateSubGUI(subGui);
if (!button.hoverableText.isEmpty()) {
button.drawHover(i, j, subGui);
button.drawHover(drawX, drawY, subGui);
}
}
for (GuiNpcTextField tf : textfields.values()) {
tf.drawTextBox(i, j);
tf.drawTextBox(drawX, drawY);
if (tf.hasHoverText())
tf.drawHover(i, j, subGui);
tf.drawHover(drawX, drawY, subGui);
}
for(GuiScrollWindow guiScrollableComponent : scrollWindows.values()) {
if (guiScrollableComponent.isMouseOver(mouseX, mouseY)) {
guiScrollableComponent.drawHoverTexts(mouseX, mouseY);
if (guiScrollableComponent.isMouseOver(drawX, drawY)) {
guiScrollableComponent.drawHoverTexts(drawX, drawY);
}
}
for (GuiScreen gui : extra.values())
gui.drawScreen(i, j, f);
gui.drawScreen(drawX, drawY, f);
if (panning) {
GL11.glPopMatrix();
activePanX = 0;
activePanY = 0;
}
if (subgui != null) {
subgui.drawScreen(i, j, f);