Refactoring Phase 3: AnimPreview + AnimSet aus EditorApp extrahiert

AnimPreviewController und AnimSetController in de.blight.editor.toolset.anim/
extrahiert (~1500 Zeilen raus). EditorApp: 13.187 → 11.650 Zeilen.
ToolContext um registry-Parameter erweitert, damit AnimSetController sich
nach Picker-Dialog selbst aktivieren kann.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 22:28:09 +02:00
parent e4b0ee0dc3
commit f0a0c216f6
4 changed files with 1442 additions and 1565 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,7 @@ import java.util.function.Supplier;
* @param viewportSupplier Liefert den JME3-Viewport-StackPane
* @param assetRoot Wurzelpfad der blight-assets (für Dateidialoge / IO)
* @param switchToWorld Wechselt zurück zum Welteneditor (für "← Welteneditor"-Buttons)
* @param registry ToolRegistry (damit Controller sich selbst aktivieren können)
*/
public record ToolContext(
SharedInput input,
@@ -36,5 +37,6 @@ public record ToolContext(
Consumer<Node> setMainToolbar,
Supplier<StackPane> viewportSupplier,
Path assetRoot,
Runnable switchToWorld
Runnable switchToWorld,
ToolRegistry registry
) {}

View File

@@ -0,0 +1,190 @@
package de.blight.editor.toolset.anim;
import de.blight.editor.ToolContext;
import de.blight.editor.ToolController;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import static de.blight.editor.toolset.UiHelper.*;
public class AnimPreviewController implements ToolController {
private final ToolContext ctx;
private ImageView animPreviewView;
private ListView<String> animClipListView;
private Label animPreviewStatusLabel;
private ComboBox<String> animPreviewModelCombo; // immer null — dead-code-Stub, null-Guards schützen
private String animCurrentModelPath = null;
private double animPrevDragX, animPrevDragY;
public AnimPreviewController(ToolContext ctx) {
this.ctx = ctx;
}
@Override
public String toolId() { return "animpreview"; }
@Override
public void activate() {
ctx.input().animPreviewSequenceQueue.clear();
animPreviewView = new ImageView(ctx.input().animPreviewImage);
animPreviewView.setPreserveRatio(true);
animPreviewView.setSmooth(true);
StackPane previewPane = new StackPane(animPreviewView);
previewPane.setStyle("-fx-background-color: #2e2e38;");
previewPane.widthProperty().addListener((obs, ov, nv) -> {
animPreviewView.setFitWidth(nv.doubleValue());
ctx.input().animPreviewW = Math.max(64, nv.intValue());
});
previewPane.heightProperty().addListener((obs, ov, nv) -> {
animPreviewView.setFitHeight(nv.doubleValue());
ctx.input().animPreviewH = Math.max(64, nv.intValue());
});
previewPane.setOnMousePressed(e -> {
animPrevDragX = e.getSceneX();
animPrevDragY = e.getSceneY();
});
previewPane.setOnMouseDragged(e -> {
double dx = e.getSceneX() - animPrevDragX;
double dy = e.getSceneY() - animPrevDragY;
animPrevDragX = e.getSceneX();
animPrevDragY = e.getSceneY();
ctx.input().animPreviewRotY += (float) (dx * 0.5);
ctx.input().animPreviewRotX = (float) Math.max(-80, Math.min(80,
ctx.input().animPreviewRotX - dy * 0.3));
});
previewPane.setOnScroll(e -> {
double factor = e.getDeltaY() > 0 ? 0.9 : 1.1;
ctx.input().animPreviewZoom = (float) Math.max(0.05,
Math.min(20.0, ctx.input().animPreviewZoom * factor));
});
ctx.setCenterView().accept(previewPane);
ctx.setMainToolbar().accept(buildAnimPreviewToolBar());
ctx.setRightPanel().accept(buildAnimPreviewPanel());
}
@Override
public void deactivate() {}
@Override
public void onFrame() {
if (ctx.input().animPreviewResized) {
ctx.input().animPreviewResized = false;
if (animPreviewView != null) {
animPreviewView.setImage(ctx.input().animPreviewImage);
}
}
java.util.List<String> newClips = ctx.input().animPreviewClips.getAndSet(null);
if (newClips != null && animClipListView != null) {
animClipListView.getItems().setAll(newClips);
if (!newClips.isEmpty()) animClipListView.getSelectionModel().selectFirst();
}
String loadedPath = ctx.input().animPreviewLoadedPath.getAndSet(null);
if (loadedPath != null) {
animCurrentModelPath = loadedPath;
}
String animStatus = ctx.input().animPreviewStatus;
if (animStatus != null) {
ctx.input().animPreviewStatus = null;
if (animPreviewStatusLabel != null) animPreviewStatusLabel.setText(animStatus);
}
String animOp = ctx.input().animOpStatus;
if (animOp != null) {
ctx.input().animOpStatus = null;
if (animPreviewStatusLabel != null) animPreviewStatusLabel.setText(animOp);
}
}
/** Wird vom EditorApp-Baum-Handler aufgerufen, wenn ein Modell angeklickt wird. */
public void setStatusText(String text) {
if (animPreviewStatusLabel != null) animPreviewStatusLabel.setText(text);
}
/** Wird vom EditorApp-Baum-Handler aufgerufen, bevor ein neues Modell geladen wird. */
public void clearClipList() {
if (animClipListView != null) animClipListView.getItems().clear();
}
private ToolBar buildAnimPreviewToolBar() {
Button backBtn = new Button("← Welteneditor");
backBtn.setOnAction(e -> ctx.switchToWorld().run());
Label label = new Label("Animations Viewer");
label.setStyle("-fx-font-weight: bold; -fx-font-size: 13;");
ToolBar tb = new ToolBar();
tb.getItems().addAll(backBtn, new Separator(Orientation.VERTICAL), label);
return tb;
}
private VBox buildAnimPreviewPanel() {
VBox inner = new VBox(8);
inner.setPadding(new Insets(10));
// ── Animationen ────────────────────────────────────────────────────────
inner.getChildren().addAll(sectionTitle("Animationen"), new Separator());
animClipListView = new ListView<>();
animClipListView.setPrefHeight(200);
animClipListView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
Button playBtn = new Button("▶ Abspielen");
Button stopBtn = new Button("■ Stop");
playBtn.setMaxWidth(Double.MAX_VALUE);
stopBtn.setMaxWidth(Double.MAX_VALUE);
playBtn.setStyle("-fx-font-weight: bold;");
playBtn.setOnAction(e -> {
String clip = animClipListView.getSelectionModel().getSelectedItem();
if (clip != null) ctx.input().animPreviewPlayClip = clip;
});
stopBtn.setOnAction(e -> ctx.input().animPreviewPlayClip = "");
animClipListView.setOnMouseClicked(e -> {
if (e.getClickCount() == 2) {
String clip = animClipListView.getSelectionModel().getSelectedItem();
if (clip != null) ctx.input().animPreviewPlayClip = clip;
}
});
HBox btnRow = new HBox(6, playBtn, stopBtn);
HBox.setHgrow(playBtn, Priority.ALWAYS);
HBox.setHgrow(stopBtn, Priority.ALWAYS);
inner.getChildren().addAll(animClipListView, btnRow);
// ── Steuerung ──────────────────────────────────────────────────────────
inner.getChildren().addAll(new Separator(), sectionTitle("Steuerung"), new Separator());
CheckBox loopCB = new CheckBox("Loop");
loopCB.setSelected(true);
loopCB.setOnAction(e -> ctx.input().animPreviewLoop = loopCB.isSelected());
Label speedLbl = new Label("Geschwindigkeit");
Slider speedSlider = new Slider(0.1, 3.0, 1.0);
speedSlider.setShowTickMarks(true);
speedSlider.setShowTickLabels(true);
speedSlider.setMajorTickUnit(1.0);
speedSlider.valueProperty().addListener((obs, ov, nv) ->
ctx.input().animPreviewSpeed = nv.floatValue());
inner.getChildren().addAll(loopCB, speedLbl, withField(speedSlider, "%.2f"));
// ── Status ─────────────────────────────────────────────────────────────
inner.getChildren().add(new Separator());
animPreviewStatusLabel = new Label("");
animPreviewStatusLabel.setWrapText(true);
animPreviewStatusLabel.setStyle("-fx-font-size: 11; -fx-text-fill: #c00;");
inner.getChildren().add(animPreviewStatusLabel);
ScrollPane scroll = new ScrollPane(inner);
scroll.setFitToWidth(true);
scroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scroll.setStyle("-fx-background-color: transparent; -fx-background: transparent;");
VBox panel = new VBox(scroll);
VBox.setVgrow(scroll, Priority.ALWAYS);
panel.setPrefWidth(270);
panel.setStyle("-fx-background-color: #f0f0f0; -fx-border-color: #ccc; -fx-border-width: 0 0 0 1;");
return panel;
}
}

File diff suppressed because it is too large Load Diff