Skip to content
Merged
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
99 changes: 84 additions & 15 deletions src/main/java/top/fpsmaster/ui/custom/Component.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,22 @@
public class Component {
private static final Color STENCIL_MASK_COLOR = new Color(255, 255, 255, 255);

private static final float MIN_SCALE = 0.5f;
private static final float MAX_SCALE = 4.5f;
private static final float HANDLE_SIZE = 6f;
/** Cursor travel, in screen pixels, needed to change the scale by 1x. */
private static final float RESIZE_SENSITIVITY = 1f / 60f;

private float dragX = 0f;

private float dragY = 0f;

private float resizeStartScale = 1f;

private int resizeStartMouseX;

private int resizeStartMouseY;

public InterfaceModule mod;

public float x = 0f;
Expand Down Expand Up @@ -197,38 +209,55 @@ public void display(ScaledResolution sr, int mouseX, int mouseY) {
if ((Utility.mc.currentScreen instanceof GuiChat || Utility.mc.currentScreen instanceof MainPanel)) {
float scaledWidth = width * scale;
float scaledHeight = height * scale;
boolean drag = FPSMaster.componentsManager.dragLock.equals(mod.name);
ComponentsManager manager = FPSMaster.componentsManager;
boolean drag = manager.dragTarget == this;
boolean hovered = Hover.is(rX, rY, scaledWidth, scaledHeight, mouseX, mouseY);
boolean overHandle = allowScale && isOverResizeHandle(rX, rY, scaledWidth, scaledHeight, mouseX, mouseY);

alpha = (float) ((Hover.is(rX, rY, scaledWidth, scaledHeight, mouseX, mouseY) || drag) ?
alpha = (float) ((hovered || overHandle || drag) ?
AnimMath.base(alpha, 1f, 0.2f) : AnimMath.base(alpha, 0.0f, 0.2f));

Rects.fill(rX - 2, rY - 2, scaledWidth + 4, scaledHeight + 4, new Color(0, 0, 0, (int) (alpha * 80)));
draw(rX, rY);
GL11.glColor4f(1, 1, 1, 1);
if (!Mouse.isButtonDown(0)) {
FPSMaster.componentsManager.dragLock = "";

if (alpha > 0.01f && hasResizeHandle(scaledWidth, scaledHeight)) {
drawResizeHandle(rX, rY, scaledWidth, scaledHeight,
overHandle || (drag && manager.dragMode == ComponentsManager.DragMode.RESIZE));
}
if (Hover.is(rX, rY, scaledWidth, scaledHeight, mouseX, mouseY) || drag) {

if (hovered || overHandle || drag) {
if (Utility.mc.currentScreen instanceof MainPanel && ((MainPanel) Utility.mc.currentScreen).hasPointerCapture())
return;
if (allowScale && ClientSettings.isZoomBindDown()) {
int dWheel = Mouse.getDWheel();
if (dWheel > 0) scaleUp();
else if (dWheel < 0) scaleDown();
}
FPSMaster.fontManager.s14.drawString(FPSMaster.i18n.get(mod.name.toLowerCase()) + " " + (scale * 10) / 10f + "x", rX, rY - 10, new Color(255, 255, 255, (int) (alpha * 255)).getRGB());
FPSMaster.fontManager.s14.drawString(FPSMaster.i18n.get(mod.name.toLowerCase()) + " " + Math.round(scale * 10) / 10f + "x", rX, rY - 10, new Color(255, 255, 255, (int) (alpha * 255)).getRGB());

if (!Mouse.isButtonDown(0)) return;

if (!drag && FPSMaster.componentsManager.dragLock.isEmpty()) {
dragX = mouseX - rX;
dragY = mouseY - rY;
FPSMaster.componentsManager.dragLock = mod.name;
if (manager.dragTarget == null) {
manager.dragTarget = this;
if (overHandle) {
manager.dragMode = ComponentsManager.DragMode.RESIZE;
resizeStartScale = scale;
resizeStartMouseX = mouseX;
resizeStartMouseY = mouseY;
} else {
manager.dragMode = ComponentsManager.DragMode.MOVE;
dragX = mouseX - rX;
dragY = mouseY - rY;
}
}

if (FPSMaster.componentsManager.dragLock.equals(mod.name)) {
move(mouseX, mouseY);
FPSMaster.componentsManager.dragLock = mod.name;
if (manager.dragTarget == this) {
if (manager.dragMode == ComponentsManager.DragMode.RESIZE) {
resizeTo(mouseX, mouseY);
} else {
move(mouseX, mouseY);
}
}
}
} else {
Expand All @@ -237,11 +266,51 @@ public void display(ScaledResolution sr, int mouseX, int mouseY) {
}

public void scaleUp() {
if (scale < 4.5f) scale = (int) (scale * 10 + 1) / 10f;
if (scale < MAX_SCALE) scale = (int) (scale * 10 + 1) / 10f;
}

public void scaleDown() {
if (scale > 0.5f) scale = (int) (scale * 10 - 1) / 10f;
if (scale > MIN_SCALE) scale = (int) (scale * 10 - 1) / 10f;
}

/**
* The handle is suppressed on components smaller than this, where its grab area would swallow most
* of the body and leave no room to start a move.
*/
private boolean hasResizeHandle(float scaledWidth, float scaledHeight) {
return allowScale && scaledWidth >= HANDLE_SIZE * 3f && scaledHeight >= HANDLE_SIZE * 3f;
}

/** Bottom-right corner box the user grabs to resize. Sized in screen pixels, deliberately not
* scaled — otherwise a component shrunk to 0.5x would have a handle too small to hit. */
private boolean isOverResizeHandle(float rX, float rY, float scaledWidth, float scaledHeight, int mouseX, int mouseY) {
if (!hasResizeHandle(scaledWidth, scaledHeight)) {
return false;
}
return Hover.is(rX + scaledWidth - HANDLE_SIZE, rY + scaledHeight - HANDLE_SIZE,
HANDLE_SIZE * 2f, HANDLE_SIZE * 2f, mouseX, mouseY);
}

private void drawResizeHandle(float rX, float rY, float scaledWidth, float scaledHeight, boolean active) {
float handleX = rX + scaledWidth - HANDLE_SIZE;
float handleY = rY + scaledHeight - HANDLE_SIZE;
int shade = (int) (alpha * (active ? 255 : 160));
Rects.rounded(handleX, handleY, HANDLE_SIZE, HANDLE_SIZE, 2, new Color(255, 255, 255, shade).getRGB());
Rects.rounded(handleX + 1f, handleY + 1f, HANDLE_SIZE - 2f, HANDLE_SIZE - 2f, 1,
new Color(0, 0, 0, (int) (alpha * 140)).getRGB());
}

/**
* Resizes from how far the cursor has travelled since the grab, not from its distance to the
* component's origin: for right/bottom-anchored components the origin itself moves as the scale
* changes, which would feed back into the next frame's delta and run away.
*/
private void resizeTo(int mouseX, int mouseY) {
float delta = ((mouseX - resizeStartMouseX) + (mouseY - resizeStartMouseY)) / 2f;
float target = resizeStartScale + delta * RESIZE_SENSITIVITY;
// Quantise to the same 0.1 steps the scroll wheel uses so both paths agree.
target = Math.round(target * 10f) / 10f;
scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, target));
}

private void move(int x, int y) {
Expand Down
24 changes: 22 additions & 2 deletions src/main/java/top/fpsmaster/ui/custom/ComponentsManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,22 @@ public class ComponentsManager {
// List to hold all components
public final ArrayList<Component> components = new ArrayList<>();

// Variable to track drag lock state
public String dragLock = "";
/** What a drag gesture is doing to {@link #dragTarget}. */
public enum DragMode {
MOVE,
RESIZE
}

/**
* The component currently being dragged, or {@code null}. Previously this was the module's name as
* a String, which cost a string compare per component per frame and could collide when a component
* fell back to a synthesised module. More importantly the "mouse released → unlock" rule was
* evaluated inside each component's own display(), so if every HUD element happened to be hidden
* mid-drag the lock was never cleared and nothing could be dragged again.
*/
public Component dragTarget;

public DragMode dragMode = DragMode.MOVE;

// Initialize all components
public void init() {
Expand Down Expand Up @@ -143,6 +157,12 @@ public void draw(int mouseX, int mouseY) {

GuiScale.fixScale();

// Releasing the button ends any drag — decided once per frame here rather than inside each
// component, so it holds even when no component is visible to run the check.
if (!org.lwjgl.input.Mouse.isButtonDown(0)) {
dragTarget = null;
}

// Draw all components that should be displayed
int finalMouseX = mouseX;
int finalMouseY = mouseY;
Expand Down
Loading