Umbau und Konsolidierung der Tools und der Oberfläche abgeschlossen
This commit is contained in:
@@ -102,6 +102,25 @@ public class EditorApp extends Application {
|
||||
// Content-Baum (Instanzfeld, damit wir Knoten gezielt expandieren/befüllen können)
|
||||
private javafx.scene.control.TreeView<String> contentTreeView;
|
||||
private javafx.scene.control.TreeItem<String> contentCharNode;
|
||||
private javafx.scene.control.TreeItem<String> contentQuestNode;
|
||||
private javafx.scene.control.TreeItem<String> contentItemNode;
|
||||
private javafx.scene.control.TreeItem<String> contentFractionNode;
|
||||
private javafx.scene.control.TreeItem<String> contentAreaNode;
|
||||
private javafx.scene.control.TreeItem<String> contentLocationNode;
|
||||
private javafx.scene.control.TreeItem<String> contentCraftingNode;
|
||||
private javafx.scene.control.TreeItem<String> contentRecipeNode;
|
||||
// aktive Editor-View-Referenzen (für Baum-Selektion und Neu/Löschen)
|
||||
private de.blight.editor.ui.QuestEditorView activeQuestView;
|
||||
private de.blight.editor.ui.ItemEditorView activeItemView;
|
||||
private de.blight.editor.ui.FractionEditorView activeFractionView;
|
||||
private de.blight.editor.ui.AreaEditorView activeAreaView;
|
||||
private de.blight.editor.ui.LocationEditorView activeLocationView;
|
||||
private de.blight.editor.ui.CraftingTableEditorView activeCraftingView;
|
||||
private de.blight.editor.ui.RecipeEditorView activeRecipeView;
|
||||
private Button contentNewBtn;
|
||||
private Button contentDeleteBtn;
|
||||
private javafx.scene.Node worldBottomBar;
|
||||
private boolean contentTreeUpdating = false;
|
||||
|
||||
private ImageView treePreviewView; // aktualisiert wenn JME3 Bild neu erstellt
|
||||
private StackPane plantPreviewPanel; // einmaliges Vorschau-Panel für alle Generatoren
|
||||
@@ -183,7 +202,6 @@ public class EditorApp extends Application {
|
||||
private String animSetPendingPlayClip = null;
|
||||
private ComboBox<String> animSetModelCombo;
|
||||
private boolean animSetDirty = false;
|
||||
private boolean charDirty = false;
|
||||
private String animSetCurrentName = null;
|
||||
private Path animSetCurrentDir = null;
|
||||
// Anim-Offset-Editor (innerhalb AnimSet-Editor)
|
||||
@@ -442,7 +460,8 @@ public class EditorApp extends Application {
|
||||
setupAssetOverlay();
|
||||
root.setCenter(centerStack);
|
||||
root.setRight(toolPanel);
|
||||
root.setBottom(buildBottomBox());
|
||||
worldBottomBar = buildBottomBox();
|
||||
root.setBottom(worldBottomBar);
|
||||
|
||||
Scene scene = new Scene(root, 1280, 760);
|
||||
java.net.URL cssUrl = getClass().getResource("/editor.css");
|
||||
@@ -517,17 +536,6 @@ public class EditorApp extends Application {
|
||||
stage.setMinWidth(900);
|
||||
stage.setMinHeight(600);
|
||||
stage.setOnCloseRequest(e -> {
|
||||
if (animSetDirty || charDirty) {
|
||||
Alert confirm = new Alert(Alert.AlertType.CONFIRMATION,
|
||||
"Es gibt ungespeicherte Änderungen.\nTrotzdem beenden?",
|
||||
ButtonType.YES, ButtonType.NO);
|
||||
confirm.initOwner(stage);
|
||||
confirm.setHeaderText("Ungespeicherte Änderungen");
|
||||
confirm.showAndWait().ifPresent(btn -> {
|
||||
if (btn != ButtonType.YES) e.consume();
|
||||
});
|
||||
if (e.isConsumed()) return;
|
||||
}
|
||||
saveCameraPrefs();
|
||||
if (jmeApp != null) jmeApp.stop();
|
||||
Platform.exit();
|
||||
@@ -940,6 +948,7 @@ public class EditorApp extends Application {
|
||||
boolean wasObjEditor = "objecteditor".equals(currentTool);
|
||||
boolean wasModelEditor = "modeleditor".equals(currentTool);
|
||||
currentTool = "world";
|
||||
if (jmeApp != null) jmeApp.setRenderThrottled(false);
|
||||
root.setLeft(null); // ggf. gesetzten Content-Tree entfernen
|
||||
topBar.getChildren().set(1, worldToolBar);
|
||||
if (routineJmeSlot != null) {
|
||||
@@ -954,6 +963,7 @@ public class EditorApp extends Application {
|
||||
setCenterView(worldViewport);
|
||||
}
|
||||
root.setRight(toolPanel);
|
||||
root.setBottom(worldBottomBar);
|
||||
if (wasObjEditor && (input.activeLayer == SharedInput.LAYER_OBJECTS
|
||||
|| input.activeLayer == SharedInput.LAYER_OBJECTS_EDIT)) {
|
||||
input.activeLayer = 0;
|
||||
@@ -11599,7 +11609,6 @@ public class EditorApp extends Application {
|
||||
else routineEditorView.loadNpc(new de.blight.common.model.NPC());
|
||||
}
|
||||
if (charEditorStatusLabel != null) charEditorStatusLabel.setText("Geladen: " + id);
|
||||
charDirty = true;
|
||||
} catch (Exception e) {
|
||||
if (charEditorStatusLabel != null) charEditorStatusLabel.setText("Fehler: " + e.getMessage());
|
||||
}
|
||||
@@ -11663,7 +11672,6 @@ public class EditorApp extends Application {
|
||||
|
||||
try {
|
||||
de.blight.common.model.CharacterIO.save(c, charDir);
|
||||
charDirty = false;
|
||||
refreshCharacterList();
|
||||
if (charEditorStatusLabel != null) charEditorStatusLabel.setText("Gespeichert: " + id);
|
||||
} catch (Exception e) {
|
||||
@@ -11909,7 +11917,7 @@ public class EditorApp extends Application {
|
||||
|
||||
contentEditorRoot = new BorderPane();
|
||||
contentEditorRoot.setTop(buildContentTopBar());
|
||||
contentEditorRoot.setLeft(buildContentTree());
|
||||
contentEditorRoot.setLeft(buildContentTreePanel());
|
||||
contentEditorRoot.setCenter(new Label("Wähle einen Bereich in der Toolbar."));
|
||||
|
||||
Scene scene = new Scene(contentEditorRoot, 1200, 750);
|
||||
@@ -11947,8 +11955,10 @@ public class EditorApp extends Application {
|
||||
/** Wechselt im 1-Fenster-Modus zur Content-Editor-Ansicht in der Welt-Stage. */
|
||||
private void switchToContentEditor() {
|
||||
currentTool = "content";
|
||||
if (jmeApp != null) jmeApp.setRenderThrottled(true);
|
||||
topBar.getChildren().set(1, buildSingleWindowContentToolBar());
|
||||
root.setLeft(buildContentTree());
|
||||
root.setLeft(buildContentTreePanel());
|
||||
root.setBottom(null);
|
||||
restoreContentSection(contentLastSection);
|
||||
}
|
||||
|
||||
@@ -12036,59 +12046,322 @@ public class EditorApp extends Application {
|
||||
new javafx.scene.control.TreeItem<>("Content");
|
||||
treeRoot.setExpanded(true);
|
||||
|
||||
contentCharNode = new javafx.scene.control.TreeItem<>("Characters");
|
||||
contentCharNode.setExpanded(false);
|
||||
refreshContentCharacterTree();
|
||||
contentCharNode = new javafx.scene.control.TreeItem<>("Characters");
|
||||
contentQuestNode = new javafx.scene.control.TreeItem<>("Quests");
|
||||
contentFractionNode = new javafx.scene.control.TreeItem<>("Fraktionen");
|
||||
contentItemNode = new javafx.scene.control.TreeItem<>("Items");
|
||||
contentRecipeNode = new javafx.scene.control.TreeItem<>("Rezepte");
|
||||
contentCraftingNode = new javafx.scene.control.TreeItem<>("Crafting Tables");
|
||||
contentAreaNode = new javafx.scene.control.TreeItem<>("Areas");
|
||||
contentLocationNode = new javafx.scene.control.TreeItem<>("Locations");
|
||||
|
||||
String[] otherSections = {
|
||||
"Quests", "Fraktionen", "Items", "Rezepte", "Crafting Tables", "Areas", "Locations"
|
||||
};
|
||||
treeRoot.getChildren().add(contentCharNode);
|
||||
for (String s : otherSections) {
|
||||
javafx.scene.control.TreeItem<String> node =
|
||||
new javafx.scene.control.TreeItem<>(s);
|
||||
for (var node : new javafx.scene.control.TreeItem[]{
|
||||
contentCharNode, contentQuestNode, contentFractionNode, contentItemNode,
|
||||
contentRecipeNode, contentCraftingNode, contentAreaNode, contentLocationNode}) {
|
||||
node.setExpanded(false);
|
||||
treeRoot.getChildren().add(node);
|
||||
}
|
||||
|
||||
refreshContentCharacterTree();
|
||||
refreshContentCraftingTree();
|
||||
refreshContentItemTree();
|
||||
refreshContentRecipeTree();
|
||||
refreshContentQuestTree();
|
||||
refreshContentFractionTree();
|
||||
refreshContentAreaTree();
|
||||
refreshContentLocationTree();
|
||||
|
||||
contentTreeView = new javafx.scene.control.TreeView<>(treeRoot);
|
||||
contentTreeView.setShowRoot(false);
|
||||
contentTreeView.setPrefWidth(200);
|
||||
contentTreeView.getSelectionModel().selectedItemProperty().addListener((obs, ov, nv) -> {
|
||||
if (nv == null) return;
|
||||
javafx.scene.control.TreeItem<String> parent = nv.getParent();
|
||||
// Klick auf einen Character-Eintrag (Kind-Knoten von Characters)
|
||||
if (parent != null && parent == contentCharNode) {
|
||||
if (contentCharBtn != null && !contentCharBtn.isSelected()) contentCharBtn.fire();
|
||||
loadCharacterById(nv.getValue());
|
||||
return;
|
||||
}
|
||||
switch (nv.getValue()) {
|
||||
case "Characters" -> { if (contentCharBtn != null) contentCharBtn.fire(); }
|
||||
case "Quests" -> { if (contentQuestBtn != null) contentQuestBtn.fire(); }
|
||||
case "Fraktionen" -> { if (contentFraktionBtn != null) contentFraktionBtn.fire(); }
|
||||
case "Items" -> { if (contentItemBtn != null) contentItemBtn.fire(); }
|
||||
case "Rezepte" -> { if (contentRezeptBtn != null) contentRezeptBtn.fire(); }
|
||||
case "Crafting Tables" -> { if (contentCraftingBtn != null) contentCraftingBtn.fire(); }
|
||||
case "Areas" -> { if (contentAreaBtn != null) contentAreaBtn.fire(); }
|
||||
case "Locations" -> { if (contentLocationBtn != null) contentLocationBtn.fire(); }
|
||||
contentTreeView.setPrefWidth(420);
|
||||
contentTreeView.setMinWidth(200);
|
||||
contentTreeView.setCellFactory(tv -> new javafx.scene.control.TreeCell<>() {
|
||||
@Override protected void updateItem(String item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (empty || item == null) { setText(null); setGraphic(null); setStyle(""); return; }
|
||||
javafx.scene.control.TreeItem<String> ti = getTreeItem();
|
||||
javafx.scene.control.TreeItem<String> par = ti != null ? ti.getParent() : null;
|
||||
boolean isColored = item.contains(":") &&
|
||||
(par == contentQuestNode || par == contentItemNode || par == contentRecipeNode);
|
||||
if (isColored) {
|
||||
String prefix = item.substring(0, item.indexOf(':'));
|
||||
String id = item.substring(item.indexOf(':') + 1);
|
||||
String color;
|
||||
if (par == contentQuestNode) color = questTypeColor(prefix);
|
||||
else if (par == contentItemNode) color = de.blight.editor.ui.ItemEditorView.categoryColor(safeItemCategory(prefix));
|
||||
else color = contentRecipeColor(prefix);
|
||||
javafx.scene.shape.Rectangle box = new javafx.scene.shape.Rectangle(10, 10);
|
||||
box.setFill(javafx.scene.paint.Color.web(color));
|
||||
box.setArcWidth(3); box.setArcHeight(3);
|
||||
HBox graphic = new HBox(5, box);
|
||||
graphic.setAlignment(Pos.CENTER_LEFT);
|
||||
setText(id); setGraphic(graphic); setStyle("");
|
||||
} else {
|
||||
setText(item); setGraphic(null);
|
||||
boolean isSection = par == null || par.getParent() == null;
|
||||
setStyle(isSection ? "-fx-font-weight:bold;" : "");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
contentTreeView.getSelectionModel().selectedItemProperty().addListener((obs, ov, nv) -> {
|
||||
if (contentTreeUpdating) return;
|
||||
if (nv == null) {
|
||||
if (contentDeleteBtn != null) contentDeleteBtn.setDisable(true);
|
||||
return;
|
||||
}
|
||||
boolean isLeaf = nv.isLeaf();
|
||||
if (contentDeleteBtn != null) contentDeleteBtn.setDisable(!isLeaf);
|
||||
|
||||
javafx.scene.control.TreeItem<String> parent = nv.getParent();
|
||||
if (parent == contentCharNode) {
|
||||
if (contentCharBtn != null && !contentCharBtn.isSelected()) contentCharBtn.fire();
|
||||
loadCharacterById(nv.getValue()); return;
|
||||
}
|
||||
if (parent == contentQuestNode) {
|
||||
if (contentQuestBtn != null && !contentQuestBtn.isSelected()) contentQuestBtn.fire();
|
||||
String qVal = nv.getValue();
|
||||
String qId = qVal.contains(":") ? qVal.substring(qVal.indexOf(':') + 1) : qVal;
|
||||
if (activeQuestView != null) activeQuestView.selectById(qId); return;
|
||||
}
|
||||
if (parent == contentFractionNode) {
|
||||
if (contentFraktionBtn != null && !contentFraktionBtn.isSelected()) contentFraktionBtn.fire();
|
||||
if (activeFractionView != null) activeFractionView.selectByNameId(nv.getValue()); return;
|
||||
}
|
||||
if (parent == contentItemNode) {
|
||||
if (contentItemBtn != null && !contentItemBtn.isSelected()) contentItemBtn.fire();
|
||||
String iVal = nv.getValue();
|
||||
String iId = iVal.contains(":") ? iVal.substring(iVal.indexOf(':') + 1) : iVal;
|
||||
if (activeItemView != null) activeItemView.selectItem(iId); return;
|
||||
}
|
||||
if (parent == contentRecipeNode) {
|
||||
if (contentRezeptBtn != null && !contentRezeptBtn.isSelected()) contentRezeptBtn.fire();
|
||||
String rVal = nv.getValue();
|
||||
String rId = rVal.contains(":") ? rVal.substring(rVal.indexOf(':') + 1) : rVal;
|
||||
if (activeRecipeView != null) activeRecipeView.selectById(rId); return;
|
||||
}
|
||||
if (parent == contentCraftingNode) {
|
||||
if (contentCraftingBtn != null && !contentCraftingBtn.isSelected()) contentCraftingBtn.fire();
|
||||
if (activeCraftingView != null) activeCraftingView.selectByTypeName(nv.getValue()); return;
|
||||
}
|
||||
if (parent == contentAreaNode) {
|
||||
if (contentAreaBtn != null && !contentAreaBtn.isSelected()) contentAreaBtn.fire();
|
||||
if (activeAreaView != null) activeAreaView.selectById(nv.getValue()); return;
|
||||
}
|
||||
if (parent == contentLocationNode) {
|
||||
if (contentLocationBtn != null && !contentLocationBtn.isSelected()) contentLocationBtn.fire();
|
||||
if (activeLocationView != null) activeLocationView.selectById(nv.getValue()); return;
|
||||
}
|
||||
switch (nv.getValue()) {
|
||||
case "Characters" -> { if (contentCharBtn != null && !contentCharBtn.isSelected()) contentCharBtn.fire(); }
|
||||
case "Quests" -> { if (contentQuestBtn != null && !contentQuestBtn.isSelected()) contentQuestBtn.fire(); }
|
||||
case "Fraktionen" -> { if (contentFraktionBtn != null && !contentFraktionBtn.isSelected()) contentFraktionBtn.fire(); }
|
||||
case "Items" -> { if (contentItemBtn != null && !contentItemBtn.isSelected()) contentItemBtn.fire(); }
|
||||
case "Rezepte" -> { if (contentRezeptBtn != null && !contentRezeptBtn.isSelected()) contentRezeptBtn.fire(); }
|
||||
case "Crafting Tables" -> { if (contentCraftingBtn != null && !contentCraftingBtn.isSelected()) contentCraftingBtn.fire(); }
|
||||
case "Areas" -> { if (contentAreaBtn != null && !contentAreaBtn.isSelected()) contentAreaBtn.fire(); }
|
||||
case "Locations" -> { if (contentLocationBtn != null && !contentLocationBtn.isSelected()) contentLocationBtn.fire(); }
|
||||
}
|
||||
});
|
||||
|
||||
// Kontextmenü
|
||||
javafx.scene.control.ContextMenu ctxMenu = new javafx.scene.control.ContextMenu();
|
||||
contentTreeView.setOnContextMenuRequested(e -> {
|
||||
ctxMenu.getItems().clear();
|
||||
javafx.scene.control.TreeItem<String> sel =
|
||||
contentTreeView.getSelectionModel().getSelectedItem();
|
||||
boolean isLeaf = sel != null && sel.isLeaf();
|
||||
|
||||
javafx.scene.control.MenuItem addMi = new javafx.scene.control.MenuItem("Hinzufügen");
|
||||
addMi.setOnAction(ev -> contentNew());
|
||||
ctxMenu.getItems().add(addMi);
|
||||
|
||||
if (isLeaf) {
|
||||
javafx.scene.control.MenuItem delMi = new javafx.scene.control.MenuItem("Löschen");
|
||||
delMi.setOnAction(ev -> contentDelete());
|
||||
javafx.scene.control.MenuItem dupMi = new javafx.scene.control.MenuItem("Duplizieren");
|
||||
dupMi.setOnAction(ev -> contentDuplicate());
|
||||
ctxMenu.getItems().addAll(delMi, dupMi);
|
||||
}
|
||||
|
||||
ctxMenu.getItems().add(new javafx.scene.control.SeparatorMenuItem());
|
||||
javafx.scene.control.MenuItem refreshMi = new javafx.scene.control.MenuItem("Aktualisieren");
|
||||
refreshMi.setOnAction(ev -> contentRefresh());
|
||||
ctxMenu.getItems().add(refreshMi);
|
||||
|
||||
ctxMenu.show(contentTreeView, e.getScreenX(), e.getScreenY());
|
||||
e.consume();
|
||||
});
|
||||
|
||||
// F5 = Refresh, Delete = Löschen
|
||||
contentTreeView.setOnKeyPressed(e -> {
|
||||
if (e.getCode() == javafx.scene.input.KeyCode.F5) {
|
||||
contentRefresh(); e.consume();
|
||||
} else if (e.getCode() == javafx.scene.input.KeyCode.DELETE) {
|
||||
contentDelete(); e.consume();
|
||||
}
|
||||
});
|
||||
|
||||
return contentTreeView;
|
||||
}
|
||||
|
||||
private javafx.scene.layout.VBox buildContentTreePanel() {
|
||||
javafx.scene.control.TreeView<String> tree = buildContentTree();
|
||||
|
||||
contentNewBtn = new Button("+ Neu");
|
||||
contentDeleteBtn = new Button("- Löschen");
|
||||
Button refreshBtn = new Button("...");
|
||||
|
||||
contentNewBtn.setStyle("-fx-background-color:#2a7a2a;-fx-text-fill:white;");
|
||||
contentDeleteBtn.setStyle("-fx-background-color:#7a2a2a;-fx-text-fill:white;");
|
||||
contentDeleteBtn.setDisable(true);
|
||||
refreshBtn.setText("⟳");
|
||||
refreshBtn.setTooltip(new Tooltip("Aktualisieren (F5)"));
|
||||
refreshBtn.setMinWidth(javafx.scene.control.Control.USE_PREF_SIZE);
|
||||
|
||||
Button collapseBtn = new Button("◀");
|
||||
collapseBtn.setTooltip(new Tooltip("Alle zuklappen"));
|
||||
collapseBtn.setMinWidth(javafx.scene.control.Control.USE_PREF_SIZE);
|
||||
|
||||
contentNewBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
contentDeleteBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
HBox.setHgrow(contentNewBtn, Priority.ALWAYS);
|
||||
HBox.setHgrow(contentDeleteBtn, Priority.ALWAYS);
|
||||
|
||||
contentNewBtn.setOnAction(e -> contentNew());
|
||||
contentDeleteBtn.setOnAction(e -> contentDelete());
|
||||
refreshBtn.setOnAction(e -> contentRefresh());
|
||||
collapseBtn.setOnAction(e -> collapseAllContentSections());
|
||||
|
||||
HBox btnBar = new HBox(4, contentNewBtn, contentDeleteBtn, collapseBtn, refreshBtn);
|
||||
btnBar.setPadding(new Insets(4));
|
||||
btnBar.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
javafx.scene.layout.VBox panel = new javafx.scene.layout.VBox(btnBar, tree);
|
||||
javafx.scene.layout.VBox.setVgrow(tree, Priority.ALWAYS);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void contentNew() {
|
||||
switch (contentLastSection != null ? contentLastSection : "character") {
|
||||
case "character" -> { clearCharacterForm(); if (contentCharBtn != null && !contentCharBtn.isSelected()) contentCharBtn.fire(); }
|
||||
case "quest" -> { if (activeQuestView != null) activeQuestView.createNew(); }
|
||||
case "fraction" -> { if (activeFractionView != null) activeFractionView.createNew(); }
|
||||
case "item" -> { if (activeItemView != null) activeItemView.createNew(); }
|
||||
case "recipe" -> { if (activeRecipeView != null) activeRecipeView.createNew(); }
|
||||
case "area" -> { if (activeAreaView != null) activeAreaView.createNew(); }
|
||||
case "location" -> { if (activeLocationView != null) activeLocationView.createNew(); }
|
||||
}
|
||||
}
|
||||
|
||||
private void contentDelete() {
|
||||
if (contentTreeView == null) return;
|
||||
javafx.scene.control.TreeItem<String> sel =
|
||||
contentTreeView.getSelectionModel().getSelectedItem();
|
||||
if (sel == null || !sel.isLeaf()) return;
|
||||
|
||||
javafx.scene.control.Alert confirm = new javafx.scene.control.Alert(
|
||||
javafx.scene.control.Alert.AlertType.CONFIRMATION);
|
||||
confirm.initOwner(primaryStage);
|
||||
confirm.setTitle("Löschen bestätigen");
|
||||
confirm.setHeaderText("\"" + sel.getValue() + "\" wirklich löschen?");
|
||||
confirm.setContentText("Diese Aktion kann nicht rückgängig gemacht werden.");
|
||||
confirm.showAndWait().ifPresent(bt -> {
|
||||
if (bt != javafx.scene.control.ButtonType.OK) return;
|
||||
javafx.scene.control.TreeItem<String> parent = sel.getParent();
|
||||
if (parent == contentCharNode) deleteCharacterById(sel.getValue());
|
||||
else if (parent == contentQuestNode && activeQuestView != null) { activeQuestView.deleteCurrentItem(); refreshContentQuestTree(); }
|
||||
else if (parent == contentFractionNode && activeFractionView != null) { activeFractionView.deleteCurrentItem(); refreshContentFractionTree(); }
|
||||
else if (parent == contentItemNode && activeItemView != null) { activeItemView.deleteCurrentItem(); refreshContentItemTree(); }
|
||||
else if (parent == contentRecipeNode && activeRecipeView != null) { activeRecipeView.deleteCurrentItem(); refreshContentRecipeTree(); }
|
||||
else if (parent == contentAreaNode && activeAreaView != null) { activeAreaView.deleteCurrentItem(); refreshContentAreaTree(); }
|
||||
else if (parent == contentLocationNode && activeLocationView != null) { activeLocationView.deleteCurrentItem(); refreshContentLocationTree(); }
|
||||
else if (parent == contentCraftingNode && activeCraftingView != null) { activeCraftingView.deleteCurrentItem(); }
|
||||
});
|
||||
}
|
||||
|
||||
private void deleteCharacterById(String id) {
|
||||
Path charFile = ASSET_ROOT.resolve("character").resolve(id + ".character");
|
||||
try {
|
||||
java.nio.file.Files.deleteIfExists(charFile);
|
||||
} catch (IOException ex) {
|
||||
new javafx.scene.control.Alert(javafx.scene.control.Alert.AlertType.ERROR,
|
||||
"Fehler beim Löschen: " + ex.getMessage()).showAndWait();
|
||||
return;
|
||||
}
|
||||
refreshContentCharacterTree();
|
||||
clearCharacterForm();
|
||||
if (charEditorStatusLabel != null) charEditorStatusLabel.setText("Character gelöscht.");
|
||||
}
|
||||
|
||||
private void contentDuplicate() {
|
||||
if (contentTreeView == null) return;
|
||||
javafx.scene.control.TreeItem<String> sel =
|
||||
contentTreeView.getSelectionModel().getSelectedItem();
|
||||
if (sel == null || !sel.isLeaf()) return;
|
||||
javafx.scene.control.TreeItem<String> parent = sel.getParent();
|
||||
if (parent == contentCharNode) {
|
||||
duplicateCharacterById(sel.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void duplicateCharacterById(String sourceId) {
|
||||
javafx.scene.control.TextInputDialog dlg =
|
||||
new javafx.scene.control.TextInputDialog(sourceId + "_copy");
|
||||
dlg.initOwner(primaryStage);
|
||||
dlg.setTitle("Duplizieren");
|
||||
dlg.setHeaderText("Neue ID für die Kopie von \"" + sourceId + "\":");
|
||||
dlg.setContentText("ID:");
|
||||
dlg.showAndWait().ifPresent(newId -> {
|
||||
if (newId == null || newId.isBlank()) return;
|
||||
Path charDir = ASSET_ROOT.resolve("character");
|
||||
Path src = charDir.resolve(sourceId + ".character");
|
||||
Path dest = charDir.resolve(newId.trim() + ".character");
|
||||
try {
|
||||
java.nio.file.Files.copy(src, dest,
|
||||
java.nio.file.StandardCopyOption.REPLACE_EXISTING);
|
||||
refreshContentCharacterTree();
|
||||
loadCharacterById(newId.trim());
|
||||
// neu erstellten Knoten selektieren
|
||||
contentCharNode.getChildren().stream()
|
||||
.filter(ti -> ti.getValue().equals(newId.trim()))
|
||||
.findFirst()
|
||||
.ifPresent(ti -> contentTreeView.getSelectionModel().select(ti));
|
||||
} catch (IOException ex) {
|
||||
new javafx.scene.control.Alert(javafx.scene.control.Alert.AlertType.ERROR,
|
||||
"Fehler beim Duplizieren: " + ex.getMessage()).showAndWait();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void contentRefresh() {
|
||||
refreshContentCharacterTree();
|
||||
refreshContentQuestTree();
|
||||
refreshContentItemTree();
|
||||
refreshContentFractionTree();
|
||||
refreshContentRecipeTree();
|
||||
refreshContentAreaTree();
|
||||
refreshContentLocationTree();
|
||||
}
|
||||
|
||||
private void refreshContentCharacterTree() {
|
||||
if (contentCharNode == null) return;
|
||||
contentCharNode.getChildren().clear();
|
||||
Path charDir = ASSET_ROOT.resolve("character");
|
||||
if (!java.nio.file.Files.isDirectory(charDir)) return;
|
||||
try (var walk = java.nio.file.Files.list(charDir)) {
|
||||
walk.filter(p -> p.toString().endsWith(".character"))
|
||||
.map(p -> p.getFileName().toString().replace(".character", ""))
|
||||
.sorted()
|
||||
.forEach(id -> contentCharNode.getChildren().add(
|
||||
new javafx.scene.control.TreeItem<>(id)));
|
||||
} catch (IOException ignored) {}
|
||||
contentTreeUpdating = true;
|
||||
try {
|
||||
contentCharNode.getChildren().clear();
|
||||
Path charDir = ASSET_ROOT.resolve("character");
|
||||
if (!java.nio.file.Files.isDirectory(charDir)) return;
|
||||
java.util.List<javafx.scene.control.TreeItem<String>> items = new java.util.ArrayList<>();
|
||||
try (var walk = java.nio.file.Files.list(charDir)) {
|
||||
walk.filter(p -> p.toString().endsWith(".character"))
|
||||
.map(p -> p.getFileName().toString().replace(".character", ""))
|
||||
.sorted()
|
||||
.forEach(id -> items.add(new javafx.scene.control.TreeItem<>(id)));
|
||||
} catch (IOException ignored) {}
|
||||
contentCharNode.getChildren().addAll(items);
|
||||
} finally {
|
||||
contentTreeUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadCharacterById(String id) {
|
||||
@@ -12154,7 +12427,6 @@ public class EditorApp extends Application {
|
||||
else routineEditorView.loadNpc(new de.blight.common.model.NPC());
|
||||
}
|
||||
if (charEditorStatusLabel != null) charEditorStatusLabel.setText("Geladen: " + id);
|
||||
charDirty = true;
|
||||
} catch (Exception e) {
|
||||
if (charEditorStatusLabel != null) charEditorStatusLabel.setText("Fehler: " + e.getMessage());
|
||||
}
|
||||
@@ -12234,7 +12506,16 @@ public class EditorApp extends Application {
|
||||
catch (NumberFormatException e) { return def; }
|
||||
}
|
||||
|
||||
private void collapseAllContentSections() {
|
||||
for (javafx.scene.control.TreeItem<String> node : new javafx.scene.control.TreeItem[]{
|
||||
contentCharNode, contentQuestNode, contentFractionNode, contentItemNode,
|
||||
contentRecipeNode, contentCraftingNode, contentAreaNode, contentLocationNode}) {
|
||||
if (node != null) node.setExpanded(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildCharacterViewInContent() {
|
||||
collapseAllContentSections();
|
||||
onF5 = null;
|
||||
dialogEditorView = new de.blight.editor.ui.DialogEditorView();
|
||||
dialogEditorView.setDisable(true);
|
||||
@@ -12250,55 +12531,315 @@ public class EditorApp extends Application {
|
||||
|
||||
contentTarget().setCenter(tabs);
|
||||
contentTarget().setRight(null);
|
||||
contentTarget().setBottom(null);
|
||||
|
||||
if (contentCharNode != null) contentCharNode.setExpanded(true);
|
||||
}
|
||||
|
||||
private void buildQuestViewInContent() {
|
||||
collapseAllContentSections();
|
||||
onF5 = null;
|
||||
contentTarget().setCenter(
|
||||
new de.blight.editor.ui.QuestEditorView(ASSET_ROOT.resolve("quests")));
|
||||
activeQuestView = new de.blight.editor.ui.QuestEditorView(ASSET_ROOT.resolve("quests"));
|
||||
contentTarget().setCenter(activeQuestView);
|
||||
contentTarget().setRight(null);
|
||||
contentTarget().setBottom(buildQuestLegend());
|
||||
refreshContentQuestTree();
|
||||
if (contentQuestNode != null) contentQuestNode.setExpanded(true);
|
||||
}
|
||||
|
||||
private void buildFractionViewInContent() {
|
||||
collapseAllContentSections();
|
||||
onF5 = null;
|
||||
contentTarget().setCenter(
|
||||
new de.blight.editor.ui.FractionEditorView(ASSET_ROOT.resolve("fractions")));
|
||||
activeFractionView = new de.blight.editor.ui.FractionEditorView(ASSET_ROOT.resolve("fractions"));
|
||||
contentTarget().setCenter(activeFractionView);
|
||||
contentTarget().setRight(null);
|
||||
contentTarget().setBottom(null);
|
||||
refreshContentFractionTree();
|
||||
if (contentFractionNode != null) contentFractionNode.setExpanded(true);
|
||||
}
|
||||
|
||||
private void buildItemViewInContent() {
|
||||
collapseAllContentSections();
|
||||
onF5 = null;
|
||||
contentTarget().setCenter(
|
||||
new de.blight.editor.ui.ItemEditorView(ASSET_ROOT.resolve("items")));
|
||||
activeItemView = new de.blight.editor.ui.ItemEditorView(ASSET_ROOT.resolve("items"));
|
||||
contentTarget().setCenter(activeItemView);
|
||||
contentTarget().setRight(null);
|
||||
contentTarget().setBottom(buildItemLegend());
|
||||
refreshContentItemTree();
|
||||
if (contentItemNode != null) contentItemNode.setExpanded(true);
|
||||
}
|
||||
|
||||
private void buildRecipeViewInContent() {
|
||||
collapseAllContentSections();
|
||||
onF5 = null;
|
||||
contentTarget().setCenter(
|
||||
new de.blight.editor.ui.RecipeEditorView(ASSET_ROOT.resolve("recipes")));
|
||||
activeRecipeView = new de.blight.editor.ui.RecipeEditorView(ASSET_ROOT.resolve("recipes"));
|
||||
contentTarget().setCenter(activeRecipeView);
|
||||
contentTarget().setRight(null);
|
||||
contentTarget().setBottom(buildRecipeLegend());
|
||||
refreshContentRecipeTree();
|
||||
if (contentRecipeNode != null) contentRecipeNode.setExpanded(true);
|
||||
}
|
||||
|
||||
private void buildCraftingViewInContent() {
|
||||
collapseAllContentSections();
|
||||
onF5 = null;
|
||||
contentTarget().setCenter(
|
||||
new de.blight.editor.ui.CraftingTableEditorView(ASSET_ROOT.resolve("craftingtables")));
|
||||
activeCraftingView = new de.blight.editor.ui.CraftingTableEditorView(ASSET_ROOT.resolve("craftingtables"));
|
||||
contentTarget().setCenter(activeCraftingView);
|
||||
contentTarget().setRight(null);
|
||||
contentTarget().setBottom(null);
|
||||
if (contentCraftingNode != null) contentCraftingNode.setExpanded(true);
|
||||
}
|
||||
|
||||
private void buildAreaViewInContent() {
|
||||
collapseAllContentSections();
|
||||
onF5 = null;
|
||||
contentTarget().setCenter(new de.blight.editor.ui.AreaEditorView());
|
||||
activeAreaView = new de.blight.editor.ui.AreaEditorView();
|
||||
contentTarget().setCenter(activeAreaView);
|
||||
contentTarget().setRight(null);
|
||||
contentTarget().setBottom(null);
|
||||
refreshContentAreaTree();
|
||||
if (contentAreaNode != null) contentAreaNode.setExpanded(true);
|
||||
}
|
||||
|
||||
private void buildLocationViewInContent() {
|
||||
collapseAllContentSections();
|
||||
onF5 = null;
|
||||
contentTarget().setCenter(new de.blight.editor.ui.LocationEditorView());
|
||||
activeLocationView = new de.blight.editor.ui.LocationEditorView();
|
||||
contentTarget().setCenter(activeLocationView);
|
||||
contentTarget().setRight(null);
|
||||
contentTarget().setBottom(null);
|
||||
refreshContentLocationTree();
|
||||
if (contentLocationNode != null) contentLocationNode.setExpanded(true);
|
||||
}
|
||||
|
||||
// ── Baum-Refresh-Methoden ─────────────────────────────────────────────────
|
||||
|
||||
private void refreshContentQuestTree() {
|
||||
if (contentQuestNode == null) return;
|
||||
contentTreeUpdating = true;
|
||||
try {
|
||||
contentQuestNode.getChildren().clear();
|
||||
Path dir = ASSET_ROOT.resolve("quests");
|
||||
if (!java.nio.file.Files.isDirectory(dir)) return;
|
||||
java.util.List<de.blight.common.model.quests.Quest> allQuests =
|
||||
de.blight.common.model.quests.QuestIO.loadAll(dir);
|
||||
java.util.List<javafx.scene.control.TreeItem<String>> items = allQuests.stream()
|
||||
.sorted(java.util.Comparator
|
||||
.<de.blight.common.model.quests.Quest, String>comparing(
|
||||
q -> de.blight.common.model.quests.QuestIO.typeOf(q))
|
||||
.thenComparing(q -> q.getQuestId() != null ? q.getQuestId() : ""))
|
||||
.map(q -> {
|
||||
String type = de.blight.common.model.quests.QuestIO.typeOf(q);
|
||||
String id = q.getQuestId() != null ? q.getQuestId() : "";
|
||||
return new javafx.scene.control.TreeItem<>(type + ":" + id);
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
contentQuestNode.getChildren().addAll(items);
|
||||
} finally {
|
||||
contentTreeUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String questTypeColor(String type) {
|
||||
return switch (type) {
|
||||
case "BRING" -> "#cc5555";
|
||||
case "FOLLOW" -> "#55aacc";
|
||||
case "INTERACT" -> "#aa55cc";
|
||||
case "ITEM" -> "#55cc88";
|
||||
case "TALK" -> "#ccaa33";
|
||||
default -> "#888888";
|
||||
};
|
||||
}
|
||||
|
||||
private javafx.scene.layout.HBox buildQuestLegend() {
|
||||
javafx.scene.layout.HBox bar = new javafx.scene.layout.HBox(14);
|
||||
bar.setPadding(new javafx.geometry.Insets(5, 10, 5, 10));
|
||||
bar.setAlignment(javafx.geometry.Pos.CENTER_LEFT);
|
||||
bar.setStyle("-fx-background-color: #1a1a2a;");
|
||||
for (String[] entry : new String[][]{
|
||||
{"BRING", "Bring"}, {"FOLLOW", "Follow"}, {"INTERACT", "Interact"},
|
||||
{"ITEM", "Item"}, {"TALK", "Talk"}}) {
|
||||
javafx.scene.shape.Rectangle box = new javafx.scene.shape.Rectangle(10, 10);
|
||||
box.setFill(javafx.scene.paint.Color.web(questTypeColor(entry[0])));
|
||||
box.setArcWidth(3); box.setArcHeight(3);
|
||||
javafx.scene.control.Label lbl = new javafx.scene.control.Label(entry[1]);
|
||||
lbl.setStyle("-fx-text-fill: #aaa; -fx-font-size: 11;");
|
||||
javafx.scene.layout.HBox item = new javafx.scene.layout.HBox(4, box, lbl);
|
||||
item.setAlignment(javafx.geometry.Pos.CENTER_LEFT);
|
||||
bar.getChildren().add(item);
|
||||
}
|
||||
return bar;
|
||||
}
|
||||
|
||||
private static de.blight.common.model.ItemCategory safeItemCategory(String name) {
|
||||
try { return de.blight.common.model.ItemCategory.valueOf(name); }
|
||||
catch (IllegalArgumentException e) { return null; }
|
||||
}
|
||||
|
||||
private static String contentRecipeColor(String tableTypeName) {
|
||||
try {
|
||||
de.blight.common.model.CraftingTable.CraftingTableType t =
|
||||
de.blight.common.model.CraftingTable.CraftingTableType.valueOf(tableTypeName);
|
||||
return de.blight.editor.ui.CraftingTableEditorView.typeColor(t);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return "#666666";
|
||||
}
|
||||
}
|
||||
|
||||
private javafx.scene.layout.HBox buildLegendBar(String[][] entries,
|
||||
java.util.function.Function<String, String> colorFn) {
|
||||
javafx.scene.layout.HBox bar = new javafx.scene.layout.HBox(14);
|
||||
bar.setPadding(new javafx.geometry.Insets(5, 10, 5, 10));
|
||||
bar.setAlignment(javafx.geometry.Pos.CENTER_LEFT);
|
||||
bar.setStyle("-fx-background-color: #1a1a2a;");
|
||||
for (String[] entry : entries) {
|
||||
javafx.scene.shape.Rectangle box = new javafx.scene.shape.Rectangle(10, 10);
|
||||
box.setFill(javafx.scene.paint.Color.web(colorFn.apply(entry[0])));
|
||||
box.setArcWidth(3); box.setArcHeight(3);
|
||||
javafx.scene.control.Label lbl = new javafx.scene.control.Label(entry[1]);
|
||||
lbl.setStyle("-fx-text-fill: #aaa; -fx-font-size: 11;");
|
||||
javafx.scene.layout.HBox item = new javafx.scene.layout.HBox(4, box, lbl);
|
||||
item.setAlignment(javafx.geometry.Pos.CENTER_LEFT);
|
||||
bar.getChildren().add(item);
|
||||
}
|
||||
return bar;
|
||||
}
|
||||
|
||||
private javafx.scene.layout.HBox buildItemLegend() {
|
||||
return buildLegendBar(new String[][]{
|
||||
{"WEAPON", "Waffe"},
|
||||
{"GEAR", "Ausrüstung"},
|
||||
{"CONSUMABLES", "Verbrauchbar"},
|
||||
{"QUEST_ITEMS", "Quest"},
|
||||
{"CRAFTING_ITEMS","Crafting"},
|
||||
{"MISC_ITEMS", "Sonstiges"}
|
||||
}, k -> de.blight.editor.ui.ItemEditorView.categoryColor(safeItemCategory(k)));
|
||||
}
|
||||
|
||||
private javafx.scene.layout.HBox buildRecipeLegend() {
|
||||
return buildLegendBar(new String[][]{
|
||||
{"AlchemyTable", "Alchemie"},
|
||||
{"EnchantmentTable", "Verzauberung"},
|
||||
{"Smithy", "Schmiede"},
|
||||
{"Goldsmiths", "Goldschmied"},
|
||||
{"Workshop", "Werkstatt"},
|
||||
{"Fireplace", "Feuerstelle"},
|
||||
{"Kitchen", "Küche"},
|
||||
{"NONE", "Kein Tisch"}
|
||||
}, EditorApp::contentRecipeColor);
|
||||
}
|
||||
|
||||
private void refreshContentItemTree() {
|
||||
if (contentItemNode == null) return;
|
||||
contentTreeUpdating = true;
|
||||
try {
|
||||
contentItemNode.getChildren().clear();
|
||||
Path dir = ASSET_ROOT.resolve("items");
|
||||
if (!java.nio.file.Files.isDirectory(dir)) return;
|
||||
java.util.List<javafx.scene.control.TreeItem<String>> treeItems =
|
||||
de.blight.common.model.ItemIO.loadAll(dir).stream()
|
||||
.filter(i -> i.getItemId() != null && !i.getItemId().isBlank())
|
||||
.map(i -> {
|
||||
String cat = i.getCategory() != null ? i.getCategory().name() : "MISC_ITEMS";
|
||||
return new javafx.scene.control.TreeItem<>(cat + ":" + i.getItemId());
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
contentItemNode.getChildren().addAll(treeItems);
|
||||
} finally {
|
||||
contentTreeUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshContentFractionTree() {
|
||||
if (contentFractionNode == null) return;
|
||||
contentTreeUpdating = true;
|
||||
try {
|
||||
contentFractionNode.getChildren().clear();
|
||||
Path dir = ASSET_ROOT.resolve("fractions");
|
||||
if (!java.nio.file.Files.isDirectory(dir)) return;
|
||||
java.util.List<javafx.scene.control.TreeItem<String>> items =
|
||||
de.blight.common.model.FractionIO.loadAll(dir).stream()
|
||||
.filter(f -> f.getName() != null && !f.getName().id().isBlank())
|
||||
.map(f -> new javafx.scene.control.TreeItem<>(f.getName().id()))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
contentFractionNode.getChildren().addAll(items);
|
||||
} finally {
|
||||
contentTreeUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshContentAreaTree() {
|
||||
if (contentAreaNode == null) return;
|
||||
contentTreeUpdating = true;
|
||||
try {
|
||||
contentAreaNode.getChildren().clear();
|
||||
java.util.List<javafx.scene.control.TreeItem<String>> items;
|
||||
try {
|
||||
items = de.blight.common.AreaDefinitionIO.load().stream()
|
||||
.filter(a -> a.id() != null && !a.id().isBlank())
|
||||
.map(a -> new javafx.scene.control.TreeItem<>(a.id()))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
} catch (IOException ignored) { return; }
|
||||
contentAreaNode.getChildren().addAll(items);
|
||||
} finally {
|
||||
contentTreeUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshContentLocationTree() {
|
||||
if (contentLocationNode == null) return;
|
||||
contentTreeUpdating = true;
|
||||
try {
|
||||
contentLocationNode.getChildren().clear();
|
||||
java.util.List<javafx.scene.control.TreeItem<String>> items;
|
||||
try {
|
||||
items = de.blight.common.LocationIO.load().stream()
|
||||
.filter(l -> l.getId() != null && !l.getId().isBlank())
|
||||
.map(l -> new javafx.scene.control.TreeItem<>(l.getId()))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
} catch (IOException ignored) { return; }
|
||||
contentLocationNode.getChildren().addAll(items);
|
||||
} finally {
|
||||
contentTreeUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshContentRecipeTree() {
|
||||
if (contentRecipeNode == null) return;
|
||||
contentTreeUpdating = true;
|
||||
try {
|
||||
contentRecipeNode.getChildren().clear();
|
||||
Path dir = ASSET_ROOT.resolve("recipes");
|
||||
if (!java.nio.file.Files.isDirectory(dir)) return;
|
||||
java.util.List<javafx.scene.control.TreeItem<String>> treeItems =
|
||||
de.blight.common.model.RecipeIO.loadAll(dir).stream()
|
||||
.filter(r -> r.getCreates() != null && r.getCreates().getItemId() != null)
|
||||
.map(r -> {
|
||||
String table = (r.getTable() != null && r.getTable().getType() != null)
|
||||
? r.getTable().getType().name() : "NONE";
|
||||
return new javafx.scene.control.TreeItem<>(table + ":" + r.getCreates().getItemId());
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
contentRecipeNode.getChildren().addAll(treeItems);
|
||||
} finally {
|
||||
contentTreeUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshContentCraftingTree() {
|
||||
if (contentCraftingNode == null) return;
|
||||
contentTreeUpdating = true;
|
||||
try {
|
||||
contentCraftingNode.getChildren().clear();
|
||||
java.util.List<javafx.scene.control.TreeItem<String>> items = new java.util.ArrayList<>();
|
||||
for (de.blight.common.model.CraftingTable.CraftingTableType t :
|
||||
de.blight.common.model.CraftingTable.CraftingTableType.values()) {
|
||||
items.add(new javafx.scene.control.TreeItem<>(t.name()));
|
||||
}
|
||||
contentCraftingNode.getChildren().addAll(items);
|
||||
} finally {
|
||||
contentTreeUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,6 +60,12 @@ public class JmeEditorApp extends SimpleApplication {
|
||||
private int currentW;
|
||||
private int currentH;
|
||||
|
||||
private volatile boolean renderThrottled = false;
|
||||
|
||||
public void setRenderThrottled(boolean throttled) {
|
||||
this.renderThrottled = throttled;
|
||||
}
|
||||
|
||||
// ── NaN-sichere Comparatoren ──────────────────────────────────────────────
|
||||
|
||||
/** Transparent-Bucket: back-to-front nach distanceToEdge, NaN-sicher. */
|
||||
@@ -300,6 +306,11 @@ public class JmeEditorApp extends SimpleApplication {
|
||||
|
||||
@Override
|
||||
public void simpleUpdate(float tpf) {
|
||||
if (renderThrottled) {
|
||||
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
|
||||
return;
|
||||
}
|
||||
|
||||
// Viewport-Resize (von JavaFX angefordert)
|
||||
int[] req = input.resizeRequest.getAndSet(null);
|
||||
if (req != null && req[0] > 0 && req[1] > 0
|
||||
|
||||
@@ -25,8 +25,6 @@ public class AreaEditorView extends BorderPane {
|
||||
private final PauseTransition savePause = new PauseTransition(Duration.millis(600));
|
||||
private final String[] tracks = {"", "", ""};
|
||||
|
||||
private ListView<AreaDefinition> listView;
|
||||
private Button deleteBtn;
|
||||
private AreaDefinition current = null;
|
||||
private boolean reloading = false;
|
||||
|
||||
@@ -41,9 +39,7 @@ public class AreaEditorView extends BorderPane {
|
||||
setStyle("-fx-background-color: #1e1e2e;");
|
||||
reload();
|
||||
|
||||
SplitPane split = new SplitPane(buildListPanel(), buildFormPanel());
|
||||
split.setDividerPositions(0.28);
|
||||
setCenter(split);
|
||||
setCenter(buildFormPanel());
|
||||
}
|
||||
|
||||
// ── Auto-save ─────────────────────────────────────────────────────────────
|
||||
@@ -63,53 +59,6 @@ public class AreaEditorView extends BorderPane {
|
||||
persist();
|
||||
}
|
||||
|
||||
// ── List panel ────────────────────────────────────────────────────────────
|
||||
|
||||
private VBox buildListPanel() {
|
||||
listView = new ListView<>(areas);
|
||||
listView.setStyle("-fx-background-color: #1a1a2a; -fx-control-inner-background: #1a1a2a;");
|
||||
listView.setCellFactory(lv -> new ListCell<>() {
|
||||
@Override protected void updateItem(AreaDefinition d, boolean empty) {
|
||||
super.updateItem(d, empty);
|
||||
if (empty || d == null) { setText(null); setStyle(""); return; }
|
||||
String shortId = d.id().startsWith(PREFIX) ? d.id().substring(PREFIX.length()) : d.id();
|
||||
setText(shortId.isBlank() ? "—" : shortId);
|
||||
setStyle("-fx-text-fill: #dddddd;"
|
||||
+ " -fx-border-color: transparent transparent transparent #cc6644;"
|
||||
+ " -fx-border-width: 0 0 0 3; -fx-padding: 3 6 3 8;");
|
||||
}
|
||||
});
|
||||
listView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((obs, old, nw) -> onSelected(old, nw));
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
|
||||
Button newBtn = new Button("Neue Area");
|
||||
newBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
newBtn.setStyle("-fx-background-color: #3a6a3a; -fx-text-fill: white;");
|
||||
newBtn.setOnAction(e -> createArea());
|
||||
|
||||
deleteBtn = new Button("Löschen");
|
||||
deleteBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
deleteBtn.setStyle("-fx-background-color: #6a2a2a; -fx-text-fill: white;");
|
||||
deleteBtn.setDisable(true);
|
||||
deleteBtn.setOnAction(e -> deleteSelected());
|
||||
|
||||
Button refreshBtn = new Button("↺ Neu laden");
|
||||
refreshBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
refreshBtn.setOnAction(e -> reload());
|
||||
|
||||
HBox listButtons = new HBox(6, newBtn, deleteBtn);
|
||||
HBox.setHgrow(newBtn, Priority.ALWAYS);
|
||||
HBox.setHgrow(deleteBtn, Priority.ALWAYS);
|
||||
listButtons.setPadding(new Insets(6, 8, 6, 8));
|
||||
|
||||
VBox panel = new VBox(6, listView, listButtons, refreshBtn);
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
panel.setPadding(new Insets(8));
|
||||
panel.setStyle("-fx-background-color: #1a1a2a;");
|
||||
return panel;
|
||||
}
|
||||
|
||||
// ── Form panel ────────────────────────────────────────────────────────────
|
||||
|
||||
private ScrollPane buildFormPanel() {
|
||||
@@ -193,7 +142,6 @@ public class AreaEditorView extends BorderPane {
|
||||
if (reloading) return;
|
||||
if (old != null) { saveFormToArea(old); persist(); }
|
||||
current = nw;
|
||||
deleteBtn.setDisable(nw == null);
|
||||
if (nw != null) { formContainer.setDisable(false); loadForm(nw); }
|
||||
else { formContainer.setDisable(true); clearForm(); }
|
||||
}
|
||||
@@ -237,7 +185,7 @@ public class AreaEditorView extends BorderPane {
|
||||
private void createArea() {
|
||||
AreaDefinition d = new AreaDefinition(PREFIX + "neu_" + System.currentTimeMillis(), "", "", "");
|
||||
areas.add(d);
|
||||
listView.getSelectionModel().select(d);
|
||||
onSelected(current, d);
|
||||
}
|
||||
|
||||
private void deleteSelected() {
|
||||
@@ -246,11 +194,29 @@ public class AreaEditorView extends BorderPane {
|
||||
current = null;
|
||||
clearForm();
|
||||
formContainer.setDisable(true);
|
||||
deleteBtn.setDisable(true);
|
||||
persist();
|
||||
reload();
|
||||
}
|
||||
|
||||
public void createNew() { createArea(); }
|
||||
|
||||
public void deleteCurrentItem() { deleteSelected(); }
|
||||
|
||||
public void selectById(String id) {
|
||||
areas.stream()
|
||||
.filter(a -> id.equals(a.id()))
|
||||
.findFirst()
|
||||
.ifPresent(a -> onSelected(current, a));
|
||||
}
|
||||
|
||||
public java.util.List<String> getIds() {
|
||||
return areas.stream()
|
||||
.map(AreaDefinition::id)
|
||||
.filter(id -> id != null && !id.isBlank())
|
||||
.sorted()
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
private void persist() {
|
||||
try { AreaDefinitionIO.save(areas); }
|
||||
catch (IOException e) {
|
||||
|
||||
@@ -26,8 +26,6 @@ public class CraftingTableEditorView extends BorderPane {
|
||||
|
||||
// ── List ──────────────────────────────────────────────────────────────────
|
||||
|
||||
private ListView<CraftingTable.CraftingTableType> listView;
|
||||
private Button deleteBtn;
|
||||
private CraftingTable.CraftingTableType currentType = null;
|
||||
|
||||
// ── Form fields ───────────────────────────────────────────────────────────
|
||||
@@ -48,51 +46,7 @@ public class CraftingTableEditorView extends BorderPane {
|
||||
setStyle("-fx-background-color: #1e1e2e;");
|
||||
reloadMap();
|
||||
|
||||
SplitPane split = new SplitPane(buildListPanel(), buildFormPanel());
|
||||
split.setDividerPositions(0.28);
|
||||
setCenter(split);
|
||||
}
|
||||
|
||||
// ── List panel ────────────────────────────────────────────────────────────
|
||||
|
||||
private VBox buildListPanel() {
|
||||
listView = new ListView<>();
|
||||
listView.getItems().setAll(CraftingTable.CraftingTableType.values());
|
||||
listView.setStyle("-fx-background-color: #1a1a2a; -fx-control-inner-background: #1a1a2a;");
|
||||
listView.setCellFactory(lv -> new ListCell<>() {
|
||||
@Override protected void updateItem(CraftingTable.CraftingTableType type, boolean empty) {
|
||||
super.updateItem(type, empty);
|
||||
if (empty || type == null) { setText(null); setStyle(""); return; }
|
||||
boolean configured = shared.containsKey(type);
|
||||
String color = typeColor(type);
|
||||
String suffix = configured ? " ✓" : " —";
|
||||
setText(type.name() + suffix);
|
||||
String fillColor = configured ? "#dddddd" : "#666666";
|
||||
setStyle("-fx-text-fill: " + fillColor + ";"
|
||||
+ " -fx-border-color: transparent transparent transparent " + color + ";"
|
||||
+ " -fx-border-width: 0 0 0 3; -fx-padding: 3 6 3 8;");
|
||||
setTooltip(new Tooltip(type.name() + (configured ? " – konfiguriert" : " – nicht konfiguriert")));
|
||||
}
|
||||
});
|
||||
listView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((obs, old, nw) -> onTypeSelected(old, nw));
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
|
||||
deleteBtn = new Button("Konfiguration löschen");
|
||||
deleteBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
deleteBtn.setStyle("-fx-background-color: #6a2a2a; -fx-text-fill: white;");
|
||||
deleteBtn.setDisable(true);
|
||||
deleteBtn.setOnAction(e -> deleteSelected());
|
||||
|
||||
Button refreshBtn = new Button("↺ Neu laden");
|
||||
refreshBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
refreshBtn.setOnAction(e -> { reloadMap(); refresh(); });
|
||||
|
||||
VBox panel = new VBox(6, listView, deleteBtn, refreshBtn);
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
panel.setPadding(new Insets(8));
|
||||
panel.setStyle("-fx-background-color: #1a1a2a;");
|
||||
return panel;
|
||||
setCenter(buildFormPanel());
|
||||
}
|
||||
|
||||
// ── Form panel ────────────────────────────────────────────────────────────
|
||||
@@ -151,12 +105,10 @@ public class CraftingTableEditorView extends BorderPane {
|
||||
currentType = nw;
|
||||
if (nw == null) {
|
||||
formContainer.setDisable(true);
|
||||
deleteBtn.setDisable(true);
|
||||
clearForm();
|
||||
} else {
|
||||
formContainer.setDisable(false);
|
||||
loadFormFromType(nw);
|
||||
deleteBtn.setDisable(!shared.containsKey(nw));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +145,6 @@ public class CraftingTableEditorView extends BorderPane {
|
||||
}
|
||||
reloadMap();
|
||||
refresh();
|
||||
listView.getSelectionModel().select(currentType);
|
||||
}
|
||||
|
||||
private void deleteSelected() {
|
||||
@@ -206,7 +157,21 @@ public class CraftingTableEditorView extends BorderPane {
|
||||
}
|
||||
reloadMap();
|
||||
refresh();
|
||||
listView.getSelectionModel().select(currentType);
|
||||
}
|
||||
|
||||
public void deleteCurrentItem() { deleteSelected(); }
|
||||
|
||||
public void selectByTypeName(String name) {
|
||||
try {
|
||||
CraftingTable.CraftingTableType type = CraftingTable.CraftingTableType.valueOf(name);
|
||||
onTypeSelected(currentType, type);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
|
||||
public String[] getTypeNames() {
|
||||
return java.util.Arrays.stream(CraftingTable.CraftingTableType.values())
|
||||
.map(Enum::name)
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
private void clearForm() {
|
||||
@@ -226,16 +191,14 @@ public class CraftingTableEditorView extends BorderPane {
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
listView.refresh();
|
||||
if (currentType != null) {
|
||||
deleteBtn.setDisable(!shared.containsKey(currentType));
|
||||
if (!formContainer.isDisable()) loadFormFromType(currentType);
|
||||
if (currentType != null && !formContainer.isDisable()) {
|
||||
loadFormFromType(currentType);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
static String typeColor(CraftingTable.CraftingTableType type) {
|
||||
public static String typeColor(CraftingTable.CraftingTableType type) {
|
||||
if (type == null) return "#666666";
|
||||
return switch (type) {
|
||||
case AlchemyTable -> "#44bb88";
|
||||
|
||||
@@ -28,8 +28,6 @@ public class FractionEditorView extends BorderPane {
|
||||
// ── List ──────────────────────────────────────────────────────────────────
|
||||
|
||||
private final SortedList<Fraction> sortedFractions;
|
||||
private ListView<Fraction> listView;
|
||||
private Button deleteBtn;
|
||||
private Fraction current = null;
|
||||
|
||||
// ── Form fields ───────────────────────────────────────────────────────────
|
||||
@@ -55,57 +53,7 @@ public class FractionEditorView extends BorderPane {
|
||||
setStyle("-fx-background-color: #1e1e2e;");
|
||||
reload();
|
||||
|
||||
SplitPane split = new SplitPane(buildListPanel(), buildFormPanel());
|
||||
split.setDividerPositions(0.28);
|
||||
setCenter(split);
|
||||
}
|
||||
|
||||
// ── List panel ────────────────────────────────────────────────────────────
|
||||
|
||||
private VBox buildListPanel() {
|
||||
listView = new ListView<>(sortedFractions);
|
||||
listView.setStyle("-fx-background-color: #1a1a2a; -fx-control-inner-background: #1a1a2a;");
|
||||
listView.setCellFactory(lv -> new ListCell<>() {
|
||||
@Override protected void updateItem(Fraction f, boolean empty) {
|
||||
super.updateItem(f, empty);
|
||||
if (empty || f == null) { setText(null); setStyle(""); return; }
|
||||
String name = f.getName() != null ? f.getName().id() : "—";
|
||||
setText(name);
|
||||
setTooltip(new Tooltip("ID: " + (f.getFractionId() != null ? f.getFractionId() : "?")));
|
||||
setStyle("-fx-text-fill: #dddddd;"
|
||||
+ " -fx-border-color: transparent transparent transparent #6699cc;"
|
||||
+ " -fx-border-width: 0 0 0 3; -fx-padding: 3 6 3 8;");
|
||||
}
|
||||
});
|
||||
listView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((obs, old, nw) -> onFractionSelected(old, nw));
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
|
||||
Button newBtn = new Button("Neue Fraktion");
|
||||
newBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
newBtn.setStyle("-fx-background-color: #3a6a3a; -fx-text-fill: white;");
|
||||
newBtn.setOnAction(e -> createFraction());
|
||||
|
||||
deleteBtn = new Button("Löschen");
|
||||
deleteBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
deleteBtn.setStyle("-fx-background-color: #6a2a2a; -fx-text-fill: white;");
|
||||
deleteBtn.setDisable(true);
|
||||
deleteBtn.setOnAction(e -> deleteSelected());
|
||||
|
||||
Button refreshBtn = new Button("↺ Neu laden");
|
||||
refreshBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
refreshBtn.setOnAction(e -> reload());
|
||||
|
||||
HBox listButtons = new HBox(6, newBtn, deleteBtn);
|
||||
HBox.setHgrow(newBtn, Priority.ALWAYS);
|
||||
HBox.setHgrow(deleteBtn, Priority.ALWAYS);
|
||||
listButtons.setPadding(new Insets(6, 8, 6, 8));
|
||||
|
||||
VBox panel = new VBox(6, listView, listButtons, refreshBtn);
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
panel.setPadding(new Insets(8));
|
||||
panel.setStyle("-fx-background-color: #1a1a2a;");
|
||||
return panel;
|
||||
setCenter(buildFormPanel());
|
||||
}
|
||||
|
||||
// ── Form panel ────────────────────────────────────────────────────────────
|
||||
@@ -185,7 +133,6 @@ public class FractionEditorView extends BorderPane {
|
||||
}
|
||||
}
|
||||
current = nw;
|
||||
deleteBtn.setDisable(nw == null);
|
||||
if (nw != null) {
|
||||
formContainer.setDisable(false);
|
||||
loadFormFromFraction(nw);
|
||||
@@ -230,19 +177,17 @@ public class FractionEditorView extends BorderPane {
|
||||
Fraction f = new Fraction();
|
||||
f.setFractionId(UUID.randomUUID());
|
||||
fractions.add(f);
|
||||
listView.getSelectionModel().select(f);
|
||||
onFractionSelected(current, f);
|
||||
}
|
||||
|
||||
private void deleteSelected() {
|
||||
Fraction sel = listView.getSelectionModel().getSelectedItem();
|
||||
if (sel == null) return;
|
||||
UUID id = sel.getFractionId();
|
||||
fractions.remove(sel);
|
||||
if (current == null) return;
|
||||
UUID id = current.getFractionId();
|
||||
fractions.remove(current);
|
||||
try { FractionIO.delete(id, fractionDir); } catch (IOException ignored) {}
|
||||
current = null;
|
||||
clearForm();
|
||||
formContainer.setDisable(true);
|
||||
deleteBtn.setDisable(true);
|
||||
reload();
|
||||
}
|
||||
|
||||
@@ -250,6 +195,25 @@ public class FractionEditorView extends BorderPane {
|
||||
fractions.setAll(FractionIO.loadAll(fractionDir));
|
||||
}
|
||||
|
||||
public void createNew() { createFraction(); }
|
||||
|
||||
public void deleteCurrentItem() { deleteSelected(); }
|
||||
|
||||
public void selectByNameId(String nameId) {
|
||||
sortedFractions.stream()
|
||||
.filter(f -> f.getName() != null && nameId.equals(f.getName().id()))
|
||||
.findFirst()
|
||||
.ifPresent(f -> onFractionSelected(current, f));
|
||||
}
|
||||
|
||||
public java.util.List<String> getNameIds() {
|
||||
return sortedFractions.stream()
|
||||
.map(f -> f.getName() != null ? f.getName().id() : "")
|
||||
.filter(n -> !n.isBlank())
|
||||
.sorted()
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
private static TextReference ref(String text) {
|
||||
|
||||
@@ -36,7 +36,6 @@ public class ItemEditorView extends BorderPane {
|
||||
private final Path assetRoot;
|
||||
|
||||
private final SortedList<Item> sortedItems;
|
||||
private final ListView<Item> listView;
|
||||
private Item current = null;
|
||||
|
||||
// Form-Felder
|
||||
@@ -48,7 +47,6 @@ public class ItemEditorView extends BorderPane {
|
||||
private Spinner<Integer> goldSpinner;
|
||||
private TextField modelRefField;
|
||||
private VBox formContainer;
|
||||
private Button deleteBtn;
|
||||
private CheckBox consumableCheck;
|
||||
private VBox consumablesSection;
|
||||
private VBox effectsRows;
|
||||
@@ -65,65 +63,8 @@ public class ItemEditorView extends BorderPane {
|
||||
setStyle("-fx-background-color: #1e1e2e;");
|
||||
reload();
|
||||
|
||||
// ── Linke Seite: Liste ────────────────────────────────────────────────
|
||||
sortedItems = new SortedList<>(items, ItemIO.SORT_ORDER);
|
||||
listView = new ListView<>(sortedItems);
|
||||
listView.setStyle("-fx-background-color: #1a1a2a; -fx-control-inner-background: #1a1a2a;");
|
||||
listView.setCellFactory(lv -> new ListCell<>() {
|
||||
@Override protected void updateItem(Item item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (empty || item == null) { setText(null); setStyle(""); return; }
|
||||
String catName = item.getCategory() != null ? item.getCategory().name() : "—";
|
||||
String id = item.getItemId() != null ? item.getItemId() : "—";
|
||||
setText(id);
|
||||
String color = categoryColor(item.getCategory());
|
||||
setStyle("-fx-text-fill: #dddddd;"
|
||||
+ " -fx-border-color: transparent transparent transparent " + color + ";"
|
||||
+ " -fx-border-width: 0 0 0 3;"
|
||||
+ " -fx-padding: 3 6 3 8;");
|
||||
setTooltip(new Tooltip("[" + catName + "] " + id));
|
||||
}
|
||||
});
|
||||
listView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((obs, old, nw) -> onItemSelected(old, nw));
|
||||
|
||||
Button refreshBtn = new Button("↺");
|
||||
refreshBtn.setStyle("-fx-background-color: transparent; -fx-text-fill: #888; -fx-cursor: hand;");
|
||||
refreshBtn.setOnAction(e -> reload());
|
||||
Label titleLbl = new Label("Items");
|
||||
titleLbl.setStyle("-fx-font-weight: bold; -fx-font-size: 13; -fx-text-fill: #aaccee;");
|
||||
HBox header = new HBox(8, titleLbl, refreshBtn);
|
||||
header.setPadding(new Insets(8, 10, 8, 10));
|
||||
header.setAlignment(Pos.CENTER_LEFT);
|
||||
header.setStyle("-fx-background-color: #1a1a2a; -fx-border-color: #444; -fx-border-width: 0 0 1 0;");
|
||||
|
||||
HBox legend = buildLegend();
|
||||
legend.setPadding(new Insets(4, 8, 4, 8));
|
||||
legend.setStyle("-fx-background-color: #1a1a2a;");
|
||||
|
||||
Button newBtn = new Button("Neues Item");
|
||||
newBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
newBtn.setStyle("-fx-background-color: #3a6a3a; -fx-text-fill: white;");
|
||||
newBtn.setOnAction(e -> createItem());
|
||||
|
||||
deleteBtn = new Button("Löschen");
|
||||
deleteBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
deleteBtn.setStyle("-fx-background-color: #6a2a2a; -fx-text-fill: white;");
|
||||
deleteBtn.setDisable(true);
|
||||
deleteBtn.setOnAction(e -> deleteSelected());
|
||||
|
||||
HBox listButtons = new HBox(6, newBtn, deleteBtn);
|
||||
listButtons.setPadding(new Insets(6, 8, 6, 8));
|
||||
HBox.setHgrow(newBtn, Priority.ALWAYS);
|
||||
HBox.setHgrow(deleteBtn, Priority.ALWAYS);
|
||||
|
||||
VBox leftPane = new VBox(header, listView, legend, listButtons);
|
||||
leftPane.setPrefWidth(440);
|
||||
leftPane.setMinWidth(300);
|
||||
leftPane.setStyle("-fx-background-color: #1a1a2a; -fx-border-color: #3a3a4a; -fx-border-width: 0 1 0 0;");
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
|
||||
// ── Rechte Seite: Formular ────────────────────────────────────────────
|
||||
formContainer = buildForm();
|
||||
formContainer.setDisable(true);
|
||||
|
||||
@@ -131,7 +72,6 @@ public class ItemEditorView extends BorderPane {
|
||||
formScroll.setFitToWidth(true);
|
||||
formScroll.setStyle("-fx-background-color: #252535; -fx-background: #252535;");
|
||||
|
||||
setLeft(leftPane);
|
||||
setCenter(formScroll);
|
||||
}
|
||||
|
||||
@@ -142,7 +82,28 @@ public class ItemEditorView extends BorderPane {
|
||||
items.stream()
|
||||
.filter(i -> itemId.equals(i.getItemId()))
|
||||
.findFirst()
|
||||
.ifPresent(listView.getSelectionModel()::select);
|
||||
.ifPresent(i -> onItemSelected(current, i));
|
||||
}
|
||||
|
||||
public void createNew() { createItem(); }
|
||||
|
||||
public void deleteCurrentItem() { deleteSelected(); }
|
||||
|
||||
public java.util.List<String> getIds() {
|
||||
return sortedItems.stream()
|
||||
.map(Item::getItemId)
|
||||
.filter(id -> id != null && !id.isBlank())
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
public java.util.List<String> getEncodedIds() {
|
||||
return sortedItems.stream()
|
||||
.filter(i -> i.getItemId() != null && !i.getItemId().isBlank())
|
||||
.map(i -> {
|
||||
String cat = i.getCategory() != null ? i.getCategory().name() : "MISC_ITEMS";
|
||||
return cat + ":" + i.getItemId();
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
// ── Reload ────────────────────────────────────────────────────────────────
|
||||
@@ -156,7 +117,7 @@ public class ItemEditorView extends BorderPane {
|
||||
|
||||
// ── Category colors ───────────────────────────────────────────────────────
|
||||
|
||||
static String categoryColor(ItemCategory cat) {
|
||||
public static String categoryColor(ItemCategory cat) {
|
||||
if (cat == null) return "#666666";
|
||||
return switch (cat) {
|
||||
case WEAPON -> "#cc5555";
|
||||
@@ -366,7 +327,6 @@ public class ItemEditorView extends BorderPane {
|
||||
}
|
||||
}
|
||||
current = nw;
|
||||
deleteBtn.setDisable(nw == null);
|
||||
if (nw != null) {
|
||||
formContainer.setDisable(false);
|
||||
loadFormFromItem(nw);
|
||||
@@ -435,14 +395,13 @@ public class ItemEditorView extends BorderPane {
|
||||
item.setItemId("neues_item_" + System.currentTimeMillis());
|
||||
item.setCategory(ItemCategory.MISC_ITEMS);
|
||||
items.add(item);
|
||||
listView.getSelectionModel().select(item);
|
||||
onItemSelected(current, item);
|
||||
}
|
||||
|
||||
private void deleteSelected() {
|
||||
Item sel = listView.getSelectionModel().getSelectedItem();
|
||||
if (sel == null) return;
|
||||
String iId = sel.getItemId();
|
||||
items.remove(sel);
|
||||
if (current == null) return;
|
||||
String iId = current.getItemId();
|
||||
items.remove(current);
|
||||
if (iId != null && !iId.isBlank()) {
|
||||
try { ItemIO.delete(iId, itemDir); }
|
||||
catch (IOException e) { /* ignore */ }
|
||||
@@ -450,7 +409,6 @@ public class ItemEditorView extends BorderPane {
|
||||
current = null;
|
||||
clearForm();
|
||||
formContainer.setDisable(true);
|
||||
deleteBtn.setDisable(true);
|
||||
reload();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ public class LocationEditorView extends BorderPane {
|
||||
private final ObservableList<Location> locations = FXCollections.observableArrayList();
|
||||
private final PauseTransition savePause = new PauseTransition(Duration.millis(600));
|
||||
|
||||
private ListView<Location> listView;
|
||||
private Button deleteBtn;
|
||||
private Location current = null;
|
||||
private boolean reloading = false;
|
||||
|
||||
@@ -39,9 +37,7 @@ public class LocationEditorView extends BorderPane {
|
||||
setStyle("-fx-background-color: #1e1e2e;");
|
||||
reload();
|
||||
|
||||
SplitPane split = new SplitPane(buildListPanel(), buildFormPanel());
|
||||
split.setDividerPositions(0.28);
|
||||
setCenter(split);
|
||||
setCenter(buildFormPanel());
|
||||
}
|
||||
|
||||
// ── Auto-save ─────────────────────────────────────────────────────────────
|
||||
@@ -53,55 +49,6 @@ public class LocationEditorView extends BorderPane {
|
||||
saveFormToLocation(current);
|
||||
if (current.getId() == null || current.getId().isBlank()) return;
|
||||
persist();
|
||||
listView.refresh();
|
||||
}
|
||||
|
||||
// ── List panel ────────────────────────────────────────────────────────────
|
||||
|
||||
private VBox buildListPanel() {
|
||||
listView = new ListView<>(locations);
|
||||
listView.setStyle("-fx-background-color: #1a1a2a; -fx-control-inner-background: #1a1a2a;");
|
||||
listView.setCellFactory(lv -> new ListCell<>() {
|
||||
@Override protected void updateItem(Location loc, boolean empty) {
|
||||
super.updateItem(loc, empty);
|
||||
if (empty || loc == null) { setText(null); setStyle(""); return; }
|
||||
String shortId = loc.getId().startsWith(PREFIX)
|
||||
? loc.getId().substring(PREFIX.length()) : loc.getId();
|
||||
setText(shortId.isBlank() ? "—" : shortId);
|
||||
setStyle("-fx-text-fill: #dddddd;"
|
||||
+ " -fx-border-color: transparent transparent transparent #66aacc;"
|
||||
+ " -fx-border-width: 0 0 0 3; -fx-padding: 3 6 3 8;");
|
||||
}
|
||||
});
|
||||
listView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((obs, old, nw) -> onSelected(old, nw));
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
|
||||
Button newBtn = new Button("Neue Location");
|
||||
newBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
newBtn.setStyle("-fx-background-color: #3a6a3a; -fx-text-fill: white;");
|
||||
newBtn.setOnAction(e -> createLocation());
|
||||
|
||||
deleteBtn = new Button("Löschen");
|
||||
deleteBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
deleteBtn.setStyle("-fx-background-color: #6a2a2a; -fx-text-fill: white;");
|
||||
deleteBtn.setDisable(true);
|
||||
deleteBtn.setOnAction(e -> deleteSelected());
|
||||
|
||||
Button refreshBtn = new Button("↺ Neu laden");
|
||||
refreshBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
refreshBtn.setOnAction(e -> reload());
|
||||
|
||||
HBox listButtons = new HBox(6, newBtn, deleteBtn);
|
||||
HBox.setHgrow(newBtn, Priority.ALWAYS);
|
||||
HBox.setHgrow(deleteBtn, Priority.ALWAYS);
|
||||
listButtons.setPadding(new Insets(6, 8, 6, 8));
|
||||
|
||||
VBox panel = new VBox(6, listView, listButtons, refreshBtn);
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
panel.setPadding(new Insets(8));
|
||||
panel.setStyle("-fx-background-color: #1a1a2a;");
|
||||
return panel;
|
||||
}
|
||||
|
||||
// ── Form panel ────────────────────────────────────────────────────────────
|
||||
@@ -152,7 +99,6 @@ public class LocationEditorView extends BorderPane {
|
||||
if (reloading) return;
|
||||
if (old != null) { saveFormToLocation(old); persist(); }
|
||||
current = nw;
|
||||
deleteBtn.setDisable(nw == null);
|
||||
if (nw != null) { formContainer.setDisable(false); loadForm(nw); }
|
||||
else { formContainer.setDisable(true); clearForm(); }
|
||||
}
|
||||
@@ -187,7 +133,7 @@ public class LocationEditorView extends BorderPane {
|
||||
Location loc = new Location();
|
||||
loc.setName(new TextReference(PREFIX + "neu_" + System.currentTimeMillis()));
|
||||
locations.add(loc);
|
||||
listView.getSelectionModel().select(loc);
|
||||
onSelected(current, loc);
|
||||
}
|
||||
|
||||
private void deleteSelected() {
|
||||
@@ -196,11 +142,29 @@ public class LocationEditorView extends BorderPane {
|
||||
current = null;
|
||||
clearForm();
|
||||
formContainer.setDisable(true);
|
||||
deleteBtn.setDisable(true);
|
||||
persist();
|
||||
reload();
|
||||
}
|
||||
|
||||
public void createNew() { createLocation(); }
|
||||
|
||||
public void deleteCurrentItem() { deleteSelected(); }
|
||||
|
||||
public void selectById(String id) {
|
||||
locations.stream()
|
||||
.filter(loc -> id.equals(loc.getId()))
|
||||
.findFirst()
|
||||
.ifPresent(loc -> onSelected(current, loc));
|
||||
}
|
||||
|
||||
public java.util.List<String> getIds() {
|
||||
return locations.stream()
|
||||
.map(Location::getId)
|
||||
.filter(id -> id != null && !id.isBlank())
|
||||
.sorted()
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
private void persist() {
|
||||
try { LocationIO.save(locations); }
|
||||
catch (IOException e) {
|
||||
|
||||
@@ -31,8 +31,6 @@ public class QuestEditorView extends BorderPane {
|
||||
|
||||
// ── List ──────────────────────────────────────────────────────────────────
|
||||
|
||||
private ListView<Quest> listView;
|
||||
private Button deleteBtn;
|
||||
private Quest current = null;
|
||||
|
||||
// ── Common form fields ────────────────────────────────────────────────────
|
||||
@@ -64,55 +62,7 @@ public class QuestEditorView extends BorderPane {
|
||||
|
||||
reload();
|
||||
|
||||
SplitPane split = new SplitPane(buildListPanel(), buildFormPanel());
|
||||
split.setDividerPositions(0.28);
|
||||
setCenter(split);
|
||||
}
|
||||
|
||||
// ── List panel ────────────────────────────────────────────────────────────
|
||||
|
||||
private VBox buildListPanel() {
|
||||
listView = new ListView<>(quests);
|
||||
listView.setStyle("-fx-background-color: #1a1a2a; -fx-control-inner-background: #1a1a2a;");
|
||||
listView.setCellFactory(lv -> new ListCell<>() {
|
||||
@Override protected void updateItem(Quest q, boolean empty) {
|
||||
super.updateItem(q, empty);
|
||||
if (empty || q == null) { setText(null); setStyle(""); return; }
|
||||
String id = q.getQuestId() != null ? q.getQuestId() : "—";
|
||||
String type = QuestIO.typeOf(q);
|
||||
setText("[" + type + "] " + id);
|
||||
setStyle("-fx-text-fill: #cccccc;");
|
||||
}
|
||||
});
|
||||
listView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((obs, old, nw) -> onQuestSelected(old, nw));
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
|
||||
Button newBtn = new Button("Neue Quest");
|
||||
newBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
newBtn.setStyle("-fx-background-color: #3a6a3a; -fx-text-fill: white;");
|
||||
newBtn.setOnAction(e -> createQuest());
|
||||
|
||||
deleteBtn = new Button("Löschen");
|
||||
deleteBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
deleteBtn.setStyle("-fx-background-color: #6a2a2a; -fx-text-fill: white;");
|
||||
deleteBtn.setDisable(true);
|
||||
deleteBtn.setOnAction(e -> deleteSelected());
|
||||
|
||||
Button refreshBtn = new Button("↺ Neu laden");
|
||||
refreshBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
refreshBtn.setOnAction(e -> reload());
|
||||
|
||||
HBox listButtons = new HBox(6, newBtn, deleteBtn);
|
||||
HBox.setHgrow(newBtn, Priority.ALWAYS);
|
||||
HBox.setHgrow(deleteBtn, Priority.ALWAYS);
|
||||
listButtons.setPadding(new Insets(6, 8, 6, 8));
|
||||
|
||||
VBox panel = new VBox(6, listView, listButtons, refreshBtn);
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
panel.setPadding(new Insets(8));
|
||||
panel.setStyle("-fx-background-color: #1a1a2a;");
|
||||
return panel;
|
||||
setCenter(buildFormPanel());
|
||||
}
|
||||
|
||||
// ── Form panel ────────────────────────────────────────────────────────────
|
||||
@@ -151,12 +101,15 @@ public class QuestEditorView extends BorderPane {
|
||||
|
||||
textField = new TextField();
|
||||
textField.setPromptText("TextReference-Schlüssel");
|
||||
textField.setEditable(false);
|
||||
textField.textProperty().addListener((obs, o, n) -> scheduleSave());
|
||||
descField = new TextField();
|
||||
descField.setPromptText("TextReference-Schlüssel");
|
||||
descField.setEditable(false);
|
||||
descField.textProperty().addListener((obs, o, n) -> scheduleSave());
|
||||
successField = new TextField();
|
||||
successField.setPromptText("TextReference-Schlüssel");
|
||||
successField.setEditable(false);
|
||||
successField.textProperty().addListener((obs, o, n) -> scheduleSave());
|
||||
|
||||
form.getChildren().addAll(
|
||||
@@ -285,7 +238,6 @@ public class QuestEditorView extends BorderPane {
|
||||
}
|
||||
}
|
||||
current = nw;
|
||||
deleteBtn.setDisable(nw == null);
|
||||
if (nw != null) {
|
||||
formContainer.setDisable(false);
|
||||
loadFormFromQuest(nw);
|
||||
@@ -424,14 +376,13 @@ public class QuestEditorView extends BorderPane {
|
||||
TalkQuest q = new TalkQuest();
|
||||
q.setQuestId("neue_quest_" + System.currentTimeMillis());
|
||||
quests.add(q);
|
||||
listView.getSelectionModel().select(q);
|
||||
onQuestSelected(current, q);
|
||||
}
|
||||
|
||||
private void deleteSelected() {
|
||||
Quest sel = listView.getSelectionModel().getSelectedItem();
|
||||
if (sel == null) return;
|
||||
String qId = sel.getQuestId();
|
||||
quests.remove(sel);
|
||||
if (current == null) return;
|
||||
String qId = current.getQuestId();
|
||||
quests.remove(current);
|
||||
if (qId != null && !qId.isBlank()) {
|
||||
try { QuestIO.delete(qId, questDir); }
|
||||
catch (IOException e) { /* ignore */ }
|
||||
@@ -439,7 +390,6 @@ public class QuestEditorView extends BorderPane {
|
||||
current = null;
|
||||
clearForm();
|
||||
formContainer.setDisable(true);
|
||||
deleteBtn.setDisable(true);
|
||||
reload();
|
||||
}
|
||||
|
||||
@@ -448,6 +398,25 @@ public class QuestEditorView extends BorderPane {
|
||||
quests.setAll(loaded);
|
||||
}
|
||||
|
||||
public void createNew() { createQuest(); }
|
||||
|
||||
public void deleteCurrentItem() { deleteSelected(); }
|
||||
|
||||
public void selectById(String questId) {
|
||||
quests.stream()
|
||||
.filter(q -> questId.equals(q.getQuestId()))
|
||||
.findFirst()
|
||||
.ifPresent(q -> onQuestSelected(current, q));
|
||||
}
|
||||
|
||||
public java.util.List<String> getIds() {
|
||||
return quests.stream()
|
||||
.map(Quest::getQuestId)
|
||||
.filter(id -> id != null && !id.isBlank())
|
||||
.sorted()
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
private static Label sectionTitle(String text) {
|
||||
|
||||
@@ -31,8 +31,6 @@ public class RecipeEditorView extends BorderPane {
|
||||
// ── List ──────────────────────────────────────────────────────────────────
|
||||
|
||||
private final SortedList<Recipe> sortedRecipes;
|
||||
private ListView<Recipe> listView;
|
||||
private Button deleteBtn;
|
||||
private Recipe current = null;
|
||||
private String oldFileId = null;
|
||||
|
||||
@@ -63,60 +61,7 @@ public class RecipeEditorView extends BorderPane {
|
||||
setStyle("-fx-background-color: #1e1e2e;");
|
||||
reload();
|
||||
|
||||
SplitPane split = new SplitPane(buildListPanel(), buildFormPanel());
|
||||
split.setDividerPositions(0.28);
|
||||
setCenter(split);
|
||||
}
|
||||
|
||||
// ── List panel ────────────────────────────────────────────────────────────
|
||||
|
||||
private VBox buildListPanel() {
|
||||
listView = new ListView<>(sortedRecipes);
|
||||
listView.setStyle("-fx-background-color: #1a1a2a; -fx-control-inner-background: #1a1a2a;");
|
||||
listView.setCellFactory(lv -> new ListCell<>() {
|
||||
@Override protected void updateItem(Recipe r, boolean empty) {
|
||||
super.updateItem(r, empty);
|
||||
if (empty || r == null) { setText(null); setStyle(""); return; }
|
||||
String creates = r.getCreates() != null ? safe(r.getCreates().getItemId()) : "—";
|
||||
String table = r.getTable() != null && r.getTable().getType() != null
|
||||
? r.getTable().getType().name() : "Handwerk";
|
||||
setText(creates);
|
||||
setTooltip(new Tooltip("[" + table + "] erstellt: " + creates));
|
||||
String color = tableColor(r.getTable());
|
||||
setStyle("-fx-text-fill: #dddddd;"
|
||||
+ " -fx-border-color: transparent transparent transparent " + color + ";"
|
||||
+ " -fx-border-width: 0 0 0 3; -fx-padding: 3 6 3 8;");
|
||||
}
|
||||
});
|
||||
listView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((obs, old, nw) -> onRecipeSelected(old, nw));
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
|
||||
Button newBtn = new Button("Neues Rezept");
|
||||
newBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
newBtn.setStyle("-fx-background-color: #3a6a3a; -fx-text-fill: white;");
|
||||
newBtn.setOnAction(e -> createRecipe());
|
||||
|
||||
deleteBtn = new Button("Löschen");
|
||||
deleteBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
deleteBtn.setStyle("-fx-background-color: #6a2a2a; -fx-text-fill: white;");
|
||||
deleteBtn.setDisable(true);
|
||||
deleteBtn.setOnAction(e -> deleteSelected());
|
||||
|
||||
Button refreshBtn = new Button("↺ Neu laden");
|
||||
refreshBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
refreshBtn.setOnAction(e -> reload());
|
||||
|
||||
HBox listButtons = new HBox(6, newBtn, deleteBtn);
|
||||
HBox.setHgrow(newBtn, Priority.ALWAYS);
|
||||
HBox.setHgrow(deleteBtn, Priority.ALWAYS);
|
||||
listButtons.setPadding(new Insets(6, 8, 6, 8));
|
||||
|
||||
VBox panel = new VBox(6, listView, listButtons, refreshBtn);
|
||||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||||
panel.setPadding(new Insets(8));
|
||||
panel.setStyle("-fx-background-color: #1a1a2a;");
|
||||
return panel;
|
||||
setCenter(buildFormPanel());
|
||||
}
|
||||
|
||||
// ── Form panel ────────────────────────────────────────────────────────────
|
||||
@@ -252,7 +197,6 @@ public class RecipeEditorView extends BorderPane {
|
||||
}
|
||||
current = nw;
|
||||
oldFileId = nw != null ? RecipeIO.fileId(nw) : null;
|
||||
deleteBtn.setDisable(nw == null);
|
||||
if (nw != null) {
|
||||
formContainer.setDisable(false);
|
||||
loadFormFromRecipe(nw);
|
||||
@@ -355,23 +299,52 @@ public class RecipeEditorView extends BorderPane {
|
||||
creates.setItemId("neues_rezept_" + System.currentTimeMillis());
|
||||
r.setCreates(creates);
|
||||
recipes.add(r);
|
||||
listView.getSelectionModel().select(r);
|
||||
onRecipeSelected(current, r);
|
||||
}
|
||||
|
||||
private void deleteSelected() {
|
||||
Recipe sel = listView.getSelectionModel().getSelectedItem();
|
||||
if (sel == null) return;
|
||||
String fid = RecipeIO.fileId(sel);
|
||||
recipes.remove(sel);
|
||||
if (current == null) return;
|
||||
String fid = RecipeIO.fileId(current);
|
||||
recipes.remove(current);
|
||||
try { RecipeIO.delete(fid, recipeDir); } catch (IOException ignored) {}
|
||||
current = null;
|
||||
oldFileId = null;
|
||||
clearForm();
|
||||
formContainer.setDisable(true);
|
||||
deleteBtn.setDisable(true);
|
||||
reload();
|
||||
}
|
||||
|
||||
public void createNew() { createRecipe(); }
|
||||
|
||||
public void deleteCurrentItem() { deleteSelected(); }
|
||||
|
||||
public void selectById(String itemId) {
|
||||
sortedRecipes.stream()
|
||||
.filter(r -> r.getCreates() != null && itemId.equals(r.getCreates().getItemId()))
|
||||
.findFirst()
|
||||
.ifPresent(r -> onRecipeSelected(current, r));
|
||||
}
|
||||
|
||||
public java.util.List<String> getIds() {
|
||||
return sortedRecipes.stream()
|
||||
.filter(r -> r.getCreates() != null && r.getCreates().getItemId() != null)
|
||||
.map(r -> r.getCreates().getItemId())
|
||||
.sorted()
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
public java.util.List<String> getEncodedIds() {
|
||||
return sortedRecipes.stream()
|
||||
.filter(r -> r.getCreates() != null && r.getCreates().getItemId() != null)
|
||||
.map(r -> {
|
||||
String table = (r.getTable() != null && r.getTable().getType() != null)
|
||||
? r.getTable().getType().name() : "NONE";
|
||||
return table + ":" + r.getCreates().getItemId();
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
private void addComponent() {
|
||||
Dialog<String> dlg = new Dialog<>();
|
||||
dlg.setTitle("Zutat hinzufügen");
|
||||
|
||||
Reference in New Issue
Block a user