Fix UI bugs and bring the remaining screens into the new style #7
@@ -15,13 +15,21 @@ public class HudModPreview extends Element {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(RenderScope scope, ElementContext ctx) {
|
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) {
|
if (layout.renderBackground) {
|
||||||
scope.drawRoundedRectangle(0, 0, layout.width, layout.height,
|
scope.drawRoundedRectangle(0, 0, layout.width, layout.height,
|
||||||
layout.radius.value, layout.bgColor.value);
|
layout.radius.value, layout.bgColor.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
feature.renderDummy(scope);
|
mod.renderDummy(scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,71 @@
|
|||||||
package org.saturnclient.ui.screens;
|
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.ConfigManager;
|
||||||
|
import org.saturnclient.config.Theme;
|
||||||
import org.saturnclient.mod.HudMod;
|
import org.saturnclient.mod.HudMod;
|
||||||
import org.saturnclient.mod.Mod;
|
import org.saturnclient.mod.Mod;
|
||||||
import org.saturnclient.mod.ModLayout;
|
import org.saturnclient.mod.ModLayout;
|
||||||
import org.saturnclient.mod.ModManager;
|
import org.saturnclient.mod.ModManager;
|
||||||
import org.saturnclient.ui.RenderScope;
|
import org.saturnclient.ui.RenderScope;
|
||||||
import org.saturnclient.ui.SaturnScreen;
|
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 {
|
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 final List<HudMod> hudMods = new ArrayList<>();
|
||||||
|
|
||||||
private HudMod draggingMod = null;
|
private HudMod draggingMod = null;
|
||||||
private boolean resizing = false;
|
private boolean resizing = false;
|
||||||
private int offsetX, offsetY;
|
private int offsetX, offsetY;
|
||||||
private static final int RESIZE_MARGIN = 3;
|
|
||||||
|
|
||||||
public HudEditor() {
|
public HudEditor() {
|
||||||
super("HUD Editor");
|
super("HUD Editor");
|
||||||
for (Mod m : ModManager.ENABLED_MODS) {
|
for (Mod m : ModManager.ENABLED_MODS) {
|
||||||
if (m.isEnabled() && m instanceof HudMod) {
|
if (m.isEnabled() && m instanceof HudMod hudMod) {
|
||||||
hudMods.add((HudMod) m);
|
hudMods.add(hudMod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void ui() {
|
||||||
|
// Placing HUDs needs a clear view of the game.
|
||||||
|
blurBackground = false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||||
if (button == 0) {
|
if (button == 0) {
|
||||||
for (int m = hudMods.size() - 1; m >= 0; m--) {
|
HudMod mod = getModAt(mouseX, mouseY);
|
||||||
HudMod mod = hudMods.get(m);
|
|
||||||
|
if (mod != null) {
|
||||||
ModLayout dim = mod.getDimensions();
|
ModLayout dim = mod.getDimensions();
|
||||||
|
|
||||||
// Check if clicking the corner for resizing
|
|
||||||
if (isCorner(mouseX, mouseY, dim)) {
|
|
||||||
draggingMod = mod;
|
draggingMod = mod;
|
||||||
resizing = true;
|
resizing = isOnHandle(mouseX, mouseY, dim);
|
||||||
offsetX = (int) (mouseX - (dim.x.value + dim.width * dim.scale.value));
|
offsetX = (int) (mouseX - (resizing ? right(dim) : dim.x.value));
|
||||||
offsetY = (int) (mouseY - (dim.y.value + dim.height * dim.scale.value));
|
offsetY = (int) (mouseY - (resizing ? bottom(dim) : dim.y.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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return super.mouseClicked(mouseX, mouseY, button);
|
return super.mouseClicked(mouseX, mouseY, button);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,57 +84,121 @@ public class HudEditor extends SaturnScreen {
|
|||||||
ModLayout dim = draggingMod.getDimensions();
|
ModLayout dim = draggingMod.getDimensions();
|
||||||
|
|
||||||
if (resizing) {
|
if (resizing) {
|
||||||
// Scale based on cursor movement
|
// The handle follows the cursor: the scale is the new width over the unscaled width.
|
||||||
float newScale = Math.max(0.5f, ((float) (mouseX - dim.x.value) / dim.width));
|
float scale = (float) (mouseX - offsetX - dim.x.value) / dim.width;
|
||||||
dim.scale.value = newScale;
|
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 {
|
} else {
|
||||||
// Move the mod
|
|
||||||
dim.x.value = (int) mouseX - offsetX;
|
dim.x.value = (int) mouseX - offsetX;
|
||||||
dim.y.value = (int) mouseY - offsetY;
|
dim.y.value = (int) mouseY - offsetY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keepOnScreen(dim);
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
|
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ui() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(RenderScope renderScope, int mouseX, int mouseY, float delta, long elapsed) {
|
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) {
|
for (HudMod mod : hudMods) {
|
||||||
ModLayout dim = mod.getDimensions();
|
ModLayout dim = mod.getDimensions();
|
||||||
MatrixStackRef matrices = renderScope.getMatrixStack();
|
boolean active = mod == hovered;
|
||||||
matrices.push();
|
|
||||||
|
|
||||||
matrices.translate(dim.x.value, dim.y.value);
|
renderScope.getMatrixStack().push();
|
||||||
matrices.scale(dim.scale.value, dim.scale.value);
|
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
|
drawFrame(renderScope, mod, dim, active);
|
||||||
mod.renderDummy(renderScope);
|
}
|
||||||
|
|
||||||
matrices.pop();
|
drawHint(renderScope);
|
||||||
|
}
|
||||||
|
|
||||||
// Draw border around mod
|
/**
|
||||||
int borderX = dim.x.value;
|
* The outline around a HUD, its resize handle, and its name while it's
|
||||||
int borderY = dim.y.value;
|
* hovered or dragged.
|
||||||
int borderW = (int) (dim.width * dim.scale.value);
|
*/
|
||||||
int borderH = (int) (dim.height * dim.scale.value);
|
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) {
|
private void drawHint(RenderScope renderScope) {
|
||||||
return mouseX >= dim.x.value && mouseX <= dim.x.value + (dim.width * dim.scale.value) &&
|
int guiWidth = width / 2;
|
||||||
mouseY >= dim.y.value && mouseY <= dim.y.value + (dim.height * dim.scale.value);
|
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;
|
* The HUD under the mouse, preferring the one drawn on top, or null.
|
||||||
float bottomY = dim.y.value + dim.height * dim.scale.value;
|
*/
|
||||||
|
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
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user