Fix UI bugs and bring the remaining screens into the new style #7
@@ -15,13 +15,21 @@ public class HudModPreview extends Element {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope scope, ElementContext ctx) {
|
||||
ModLayout layout = feature.getDimensions();
|
||||
draw(scope, feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a HUD mod with sample content and the background it has in game,
|
||||
* at the origin and its layout's unscaled size.
|
||||
*/
|
||||
public static void draw(RenderScope scope, HudMod mod) {
|
||||
ModLayout layout = mod.getDimensions();
|
||||
|
||||
if (layout.renderBackground) {
|
||||
scope.drawRoundedRectangle(0, 0, layout.width, layout.height,
|
||||
layout.radius.value, layout.bgColor.value);
|
||||
}
|
||||
|
||||
feature.renderDummy(scope);
|
||||
mod.renderDummy(scope);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,71 @@
|
||||
package org.saturnclient.ui.screens;
|
||||
|
||||
import org.saturnclient.common.ref.render.MatrixStackRef;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.saturnclient.config.ConfigManager;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.mod.HudMod;
|
||||
import org.saturnclient.mod.Mod;
|
||||
import org.saturnclient.mod.ModLayout;
|
||||
import org.saturnclient.mod.ModManager;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.SaturnScreen;
|
||||
import org.saturnclient.ui.components.HudModPreview;
|
||||
import org.saturnclient.ui.resources.Fonts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Moves and resizes the enabled HUD mods, drawn as they are in game. Drag one
|
||||
* to move it, or drag its corner handle to resize it.
|
||||
*
|
||||
* Unlike other Saturn screens this works in GUI pixels, like the HUD itself:
|
||||
* it draws without the half scale and takes the mouse unscaled.
|
||||
*/
|
||||
public class HudEditor extends SaturnScreen {
|
||||
private static final String HINT = "Drag to move · Drag the corner to resize · Esc when done";
|
||||
|
||||
/** Size of the resize handle, and how far around it still grabs it */
|
||||
private static final int HANDLE = 4;
|
||||
private static final int HANDLE_REACH = 4;
|
||||
private static final float LABEL_SCALE = 0.4f;
|
||||
private static final float HINT_SCALE = 0.45f;
|
||||
|
||||
private final List<HudMod> hudMods = new ArrayList<>();
|
||||
|
||||
private HudMod draggingMod = null;
|
||||
private boolean resizing = false;
|
||||
private int offsetX, offsetY;
|
||||
private static final int RESIZE_MARGIN = 3;
|
||||
|
||||
public HudEditor() {
|
||||
super("HUD Editor");
|
||||
for (Mod m : ModManager.ENABLED_MODS) {
|
||||
if (m.isEnabled() && m instanceof HudMod) {
|
||||
hudMods.add((HudMod) m);
|
||||
if (m.isEnabled() && m instanceof HudMod hudMod) {
|
||||
hudMods.add(hudMod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ui() {
|
||||
// Placing HUDs needs a clear view of the game.
|
||||
blurBackground = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
if (button == 0) {
|
||||
for (int m = hudMods.size() - 1; m >= 0; m--) {
|
||||
HudMod mod = hudMods.get(m);
|
||||
HudMod mod = getModAt(mouseX, mouseY);
|
||||
|
||||
if (mod != null) {
|
||||
ModLayout dim = mod.getDimensions();
|
||||
|
||||
// Check if clicking the corner for resizing
|
||||
if (isCorner(mouseX, mouseY, dim)) {
|
||||
draggingMod = mod;
|
||||
resizing = true;
|
||||
offsetX = (int) (mouseX - (dim.x.value + dim.width * dim.scale.value));
|
||||
offsetY = (int) (mouseY - (dim.y.value + dim.height * dim.scale.value));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if clicking inside the mod
|
||||
if (isInside(mouseX, mouseY, dim)) {
|
||||
draggingMod = mod;
|
||||
resizing = false;
|
||||
offsetX = (int) (mouseX - dim.x.value);
|
||||
offsetY = (int) (mouseY - dim.y.value);
|
||||
return true;
|
||||
}
|
||||
draggingMod = mod;
|
||||
resizing = isOnHandle(mouseX, mouseY, dim);
|
||||
offsetX = (int) (mouseX - (resizing ? right(dim) : dim.x.value));
|
||||
offsetY = (int) (mouseY - (resizing ? bottom(dim) : dim.y.value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@@ -72,57 +84,121 @@ public class HudEditor extends SaturnScreen {
|
||||
ModLayout dim = draggingMod.getDimensions();
|
||||
|
||||
if (resizing) {
|
||||
// Scale based on cursor movement
|
||||
float newScale = Math.max(0.5f, ((float) (mouseX - dim.x.value) / dim.width));
|
||||
dim.scale.value = newScale;
|
||||
// The handle follows the cursor: the scale is the new width over the unscaled width.
|
||||
float scale = (float) (mouseX - offsetX - dim.x.value) / dim.width;
|
||||
float min = dim.scale.min != null ? dim.scale.min : 0.5f;
|
||||
float max = dim.scale.max != null ? dim.scale.max : 4f;
|
||||
dim.scale.value = Math.max(min, Math.min(max, scale));
|
||||
} else {
|
||||
// Move the mod
|
||||
dim.x.value = (int) mouseX - offsetX;
|
||||
dim.y.value = (int) mouseY - offsetY;
|
||||
}
|
||||
|
||||
keepOnScreen(dim);
|
||||
}
|
||||
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ui() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, int mouseX, int mouseY, float delta, long elapsed) {
|
||||
// The screen hands over the mouse in Saturn units, twice GUI pixels.
|
||||
int guiMouseX = mouseX / 2;
|
||||
int guiMouseY = mouseY / 2;
|
||||
HudMod hovered = draggingMod != null ? draggingMod : getModAt(guiMouseX, guiMouseY);
|
||||
|
||||
for (HudMod mod : hudMods) {
|
||||
ModLayout dim = mod.getDimensions();
|
||||
MatrixStackRef matrices = renderScope.getMatrixStack();
|
||||
matrices.push();
|
||||
boolean active = mod == hovered;
|
||||
|
||||
matrices.translate(dim.x.value, dim.y.value);
|
||||
matrices.scale(dim.scale.value, dim.scale.value);
|
||||
renderScope.getMatrixStack().push();
|
||||
renderScope.getMatrixStack().translate(dim.x.value, dim.y.value);
|
||||
renderScope.getMatrixStack().scale(dim.scale.value, dim.scale.value);
|
||||
HudModPreview.draw(renderScope, mod);
|
||||
renderScope.getMatrixStack().pop();
|
||||
|
||||
// Render the mod
|
||||
mod.renderDummy(renderScope);
|
||||
drawFrame(renderScope, mod, dim, active);
|
||||
}
|
||||
|
||||
matrices.pop();
|
||||
drawHint(renderScope);
|
||||
}
|
||||
|
||||
// Draw border around mod
|
||||
int borderX = dim.x.value;
|
||||
int borderY = dim.y.value;
|
||||
int borderW = (int) (dim.width * dim.scale.value);
|
||||
int borderH = (int) (dim.height * dim.scale.value);
|
||||
/**
|
||||
* The outline around a HUD, its resize handle, and its name while it's
|
||||
* hovered or dragged.
|
||||
*/
|
||||
private void drawFrame(RenderScope renderScope, HudMod mod, ModLayout dim, boolean active) {
|
||||
int x = dim.x.value;
|
||||
int y = dim.y.value;
|
||||
int w = right(dim) - x;
|
||||
int h = bottom(dim) - y;
|
||||
int color = active ? Theme.ACCENT.value : Theme.withAlpha(0.45f, Theme.FOREGROUND.value);
|
||||
|
||||
renderScope.drawBorder(borderX, borderY, borderW, borderH, 0xFFFFFFFF); // White border
|
||||
if (active) {
|
||||
renderScope.drawRectangle(x, y, w, h, Theme.withAlpha(0.08f, Theme.ACCENT.value));
|
||||
}
|
||||
|
||||
renderScope.drawBorder(x - 1, y - 1, w + 2, h + 2, color);
|
||||
renderScope.drawRectangle(x + w - HANDLE / 2, y + h - HANDLE / 2, HANDLE, HANDLE, color);
|
||||
|
||||
if (active && mod instanceof Mod named) {
|
||||
int labelHeight = (int) (Fonts.getHeight() * LABEL_SCALE);
|
||||
int labelY = y - labelHeight - 4 < 0 ? y + h + 4 : y - labelHeight - 4;
|
||||
renderScope.drawText(LABEL_SCALE, named.getName(), x, labelY, Theme.FONT.value, Theme.FOREGROUND.value);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInside(double mouseX, double mouseY, ModLayout dim) {
|
||||
return mouseX >= dim.x.value && mouseX <= dim.x.value + (dim.width * dim.scale.value) &&
|
||||
mouseY >= dim.y.value && mouseY <= dim.y.value + (dim.height * dim.scale.value);
|
||||
private void drawHint(RenderScope renderScope) {
|
||||
int guiWidth = width / 2;
|
||||
int textWidth = (int) (Fonts.getWidth(HINT, Theme.getBodyFont()) * HINT_SCALE);
|
||||
int textHeight = (int) (Fonts.getHeight() * HINT_SCALE);
|
||||
int pillWidth = textWidth + 20;
|
||||
int pillHeight = textHeight + 10;
|
||||
int x = (guiWidth - pillWidth) / 2;
|
||||
int y = 8;
|
||||
|
||||
renderScope.drawRoundedRectangle(x, y, pillWidth, pillHeight, pillHeight / 2, 1, Theme.getPanelBg(),
|
||||
Theme.getBorder());
|
||||
renderScope.drawText(HINT_SCALE, HINT, x + 10, y + 5, Theme.getBodyFont(), Theme.PRIMARY_FG.value);
|
||||
}
|
||||
|
||||
private boolean isCorner(double mouseX, double mouseY, ModLayout dim) {
|
||||
float rightX = dim.x.value + dim.width * dim.scale.value;
|
||||
float bottomY = dim.y.value + dim.height * dim.scale.value;
|
||||
/**
|
||||
* The HUD under the mouse, preferring the one drawn on top, or null.
|
||||
*/
|
||||
private HudMod getModAt(double mouseX, double mouseY) {
|
||||
for (int i = hudMods.size() - 1; i >= 0; i--) {
|
||||
ModLayout dim = hudMods.get(i).getDimensions();
|
||||
if (isOnHandle(mouseX, mouseY, dim) || isInside(mouseX, mouseY, dim)) {
|
||||
return hudMods.get(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return Math.abs(mouseX - rightX) < RESIZE_MARGIN && Math.abs(mouseY - bottomY) < RESIZE_MARGIN;
|
||||
private void keepOnScreen(ModLayout dim) {
|
||||
int guiWidth = width / 2;
|
||||
int guiHeight = height / 2;
|
||||
int w = right(dim) - dim.x.value;
|
||||
int h = bottom(dim) - dim.y.value;
|
||||
|
||||
dim.x.value = Math.max(0, Math.min(dim.x.value, guiWidth - w));
|
||||
dim.y.value = Math.max(0, Math.min(dim.y.value, guiHeight - h));
|
||||
}
|
||||
|
||||
private static int right(ModLayout dim) {
|
||||
return dim.x.value + (int) (dim.width * dim.scale.value);
|
||||
}
|
||||
|
||||
private static int bottom(ModLayout dim) {
|
||||
return dim.y.value + (int) (dim.height * dim.scale.value);
|
||||
}
|
||||
|
||||
private static boolean isInside(double mouseX, double mouseY, ModLayout dim) {
|
||||
return mouseX >= dim.x.value && mouseX <= right(dim) && mouseY >= dim.y.value && mouseY <= bottom(dim);
|
||||
}
|
||||
|
||||
private static boolean isOnHandle(double mouseX, double mouseY, ModLayout dim) {
|
||||
return Math.abs(mouseX - right(dim)) <= HANDLE_REACH && Math.abs(mouseY - bottom(dim)) <= HANDLE_REACH;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user