Ein wenig an der GUI gebastetl

This commit is contained in:
2026-08-19 22:21:32 +02:00
parent c221c26a23
commit 974ee5765e
4 changed files with 132 additions and 13 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -11326,8 +11326,10 @@ public class EditorApp extends Application {
var clip = clipCombo.getValue();
if (action != null && clip != null) {
String entry = action.name() + "" + clip;
if (!animSetActionListView.getItems().contains(entry))
if (!animSetActionListView.getItems().contains(entry)) {
animSetActionListView.getItems().add(entry);
autoSaveAnimSet();
}
}
});
}

View File

@@ -18,6 +18,12 @@ import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Image;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import com.jme3.texture.image.ColorSpace;
import com.jme3.util.BufferUtils;
import java.nio.ByteBuffer;
import de.blight.game.post.GaussianBlurFilter;
import de.blight.game.scene.WorldScene;
@@ -65,8 +71,15 @@ public abstract class MenuScreen extends BaseAppState {
private final String MAP_CLICK = "_MsClick_" + System.identityHashCode(this);
private record Btn(float x, float y, float w, float h, Runnable action) {}
private record Btn(float x, float y, float w, float h, Runnable action,
Node patchNode, BitmapText label) {}
private final List<Btn> buttons = new ArrayList<>();
private Btn pressedBtn = null;
private Btn hoveredBtn = null;
private Node hoverGlow = null;
private Texture2D glowTex = null;
private static final float PRESS_DX = 1f, PRESS_DY = -2f;
private static final float GLOW_PAD = 20f;
// ── Lifecycle ─────────────────────────────────────────────────────────────
@@ -114,6 +127,9 @@ public abstract class MenuScreen extends BaseAppState {
if (canvasNode != null) { guiNode.detachChild(canvasNode); canvasNode = null; }
panel = null;
buttons.clear();
pressedBtn = null;
hoveredBtn = null;
if (hoverGlow != null) { hoverGlow.removeFromParent(); hoverGlow = null; }
if (withBlur()) removeBlur();
}
@@ -171,7 +187,8 @@ public abstract class MenuScreen extends BaseAppState {
case ARROW -> NinePatch.buttonArrow(assets);
default -> NinePatch.button(assets);
};
parent.attachChild(patch.build(x, y, w, h, 0f));
Node patchNode = patch.build(x, y, w, h, 0f);
parent.attachChild(patchNode);
ColorRGBA col = (style == BtnStyle.DISABLED) ? UiTheme.COL_MUTED : UiTheme.COL_WHITE;
BitmapText lbl = crispText(label, UiTheme.FONT_LABEL, col);
@@ -180,21 +197,108 @@ public abstract class MenuScreen extends BaseAppState {
parent.attachChild(lbl);
if (style != BtnStyle.DISABLED && action != null) {
buttons.add(new Btn(x, y, w, h, action));
buttons.add(new Btn(x, y, w, h, action, patchNode, lbl));
}
}
// ── Update-Loop (Hover) ───────────────────────────────────────────────────
@Override
public void update(float tpf) {
if (buttons.isEmpty() || panel == null) return;
float[] v = toVirtual(app.getInputManager().getCursorPosition());
Btn hovered = null;
for (Btn b : buttons) {
if (v[0] >= b.x() && v[0] <= b.x() + b.w()
&& v[1] >= b.y() && v[1] <= b.y() + b.h()) {
hovered = b;
break;
}
}
if (hovered != hoveredBtn) {
if (hoverGlow != null) { hoverGlow.removeFromParent(); hoverGlow = null; }
hoveredBtn = hovered;
if (hoveredBtn != null) {
hoverGlow = buildHoverGlow(hoveredBtn);
panel.attachChild(hoverGlow);
}
}
if (hoverGlow != null) {
float dx = (pressedBtn == hoveredBtn) ? PRESS_DX : 0f;
float dy = (pressedBtn == hoveredBtn) ? PRESS_DY : 0f;
hoverGlow.setLocalTranslation(hoveredBtn.x() + dx, hoveredBtn.y() + dy, 0f);
}
}
private Node buildHoverGlow(Btn b) {
Geometry q = new Geometry("glow",
new Quad(b.w() + GLOW_PAD * 2f, b.h() + GLOW_PAD * 2f));
Material m = new Material(assets, "Common/MatDefs/Misc/Unshaded.j3md");
m.setTexture("ColorMap", getGlowTex());
m.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.AlphaAdditive);
q.setQueueBucket(RenderQueue.Bucket.Gui);
q.setMaterial(m);
// -GLOW_PAD damit der Glow um den Button herausragt; Node selbst wird auf Button-Pos gesetzt
q.setLocalTranslation(-GLOW_PAD, -GLOW_PAD, -0.5f);
Node g = new Node("hover-glow");
g.attachChild(q);
return g;
}
private Texture2D getGlowTex() {
if (glowTex != null) return glowTex;
int W = 64, H = 64;
ByteBuffer buf = BufferUtils.createByteBuffer(W * H * 4);
for (int py = 0; py < H; py++) {
for (int px = 0; px < W; px++) {
float u = (px + 0.5f) / W;
float v = (py + 0.5f) / H;
float a = glowFade(u) * glowFade(v);
buf.put((byte) 13); // R ≈ 0.05 × 255
buf.put((byte) 255); // G = neon
buf.put((byte) 51); // B ≈ 0.20 × 255
buf.put((byte)(int)(a * 220f)); // A peak ≈ 86 %
}
}
buf.flip();
glowTex = new Texture2D(new Image(Image.Format.RGBA8, W, H, buf, ColorSpace.Linear));
glowTex.setMagFilter(Texture.MagFilter.Bilinear);
glowTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
return glowTex;
}
private static float glowFade(float t) {
float d = Math.abs(t - 0.5f) * 2f; // 0 = Mitte, 1 = Rand
float x = 1f - Math.min(1f, d);
return x * x * (3f - 2f * x); // smoothstep
}
// ── Klick-Listener ────────────────────────────────────────────────────────
private final ActionListener clickListener = (name, pressed, tpf) -> {
if (!pressed) return;
float[] v = toVirtual(app.getInputManager().getCursorPosition());
onScreenClick(v);
for (Btn b : buttons) {
if (v[0] >= b.x() && v[0] <= b.x() + b.w()
&& v[1] >= b.y() && v[1] <= b.y() + b.h()) {
b.action().run();
return;
if (pressed) {
onScreenClick(v);
for (Btn b : buttons) {
if (v[0] >= b.x() && v[0] <= b.x() + b.w()
&& v[1] >= b.y() && v[1] <= b.y() + b.h()) {
pressedBtn = b;
b.patchNode().move(PRESS_DX, PRESS_DY, 0f);
b.label().move(PRESS_DX, PRESS_DY, 0f);
return;
}
}
} else {
if (pressedBtn != null) {
pressedBtn.patchNode().move(-PRESS_DX, -PRESS_DY, 0f);
pressedBtn.label().move(-PRESS_DX, -PRESS_DY, 0f);
Btn released = pressedBtn;
pressedBtn = null;
// Aktion nur auslösen wenn Maus noch über dem Button
if (v[0] >= released.x() && v[0] <= released.x() + released.w()
&& v[1] >= released.y() && v[1] <= released.y() + released.h()) {
released.action().run();
}
}
}
};

View File

@@ -171,6 +171,12 @@ public class NinePatch {
int t = ty >= 0 ? ty + 1 : h / 4;
int b = by >= 0 ? h - by : h / 4;
// Sicherstellen, dass Mittelbereich mindestens 1 Pixel groß ist.
// Fehlerhafte Guide-Erkennung (Pixel mit zufällig passendem Alpha) kann
// sonst l+r >= w oder t+b >= h erzeugen → Endlosschleife in den Tiling-Methoden.
if (l + r >= w) { l = w / 4; r = w / 4; }
if (t + b >= h) { t = h / 4; b = h / 4; }
return new int[]{l, r, t, b};
}
@@ -182,12 +188,16 @@ public class NinePatch {
* ty, by: Zeilenindizes der oberen/unteren Guide-Linie (PNG-Koordinaten)
*/
private static void stripGuides(BufferedImage img, int w, int h, int lx, int rx, int ty, int by) {
// Snapshot der Guide-Positionen (vor Modifikation)
// Snapshot der Guide-Positionen (vor Modifikation).
// Als Guide zählen: exakte Guide-Alphas ODER semi-transparente Pixel (alpha < 200)
// auf den bekannten Guide-Zeilen/-Spalten — entsteht durch Anti-Aliasing beim Malen.
boolean[][] isGuide = new boolean[h][w];
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int a = (img.getRGB(x, y) >> 24) & 0xff;
isGuide[y][x] = GUIDE_ALPHAS.contains(a);
boolean exactGuide = GUIDE_ALPHAS.contains(a);
boolean onGuideLine = (x == lx || x == rx || y == ty || y == by);
isGuide[y][x] = exactGuide || (onGuideLine && a < 200 && a > 0);
}
}
@@ -332,6 +342,7 @@ public class NinePatch {
float u1, float v1, float u2, float v2,
float cw, float ch,
float texW, float texH, float z) {
if (cw <= 0 || ch <= 0) return;
int iz = 0;
for (float py = sy1; py < sy2; py += ch, iz++) {
float th = Math.min(ch, sy2 - py);
@@ -352,6 +363,7 @@ public class NinePatch {
float sx1, float py, float sx2, float ph,
float u1, float u2, float vMin, float vMax,
float cw, float texW, float z) {
if (cw <= 0) return;
int ix = 0;
for (float px = sx1; px < sx2; px += cw, ix++) {
float tw = Math.min(cw, sx2 - px);
@@ -366,6 +378,7 @@ public class NinePatch {
float px, float sy1, float pw, float sy2,
float uMin, float uMax, float v1, float v2,
float ch, float texH, float z) {
if (ch <= 0) return;
int iz = 0;
for (float py = sy1; py < sy2; py += ch, iz++) {
float th = Math.min(ch, sy2 - py);