Umbau und Konsolidierung der Tools und der Oberfläche abgeschlossen

This commit is contained in:
2026-08-14 21:37:57 +02:00
parent 90599c9c7b
commit f2944c9a91
10 changed files with 799 additions and 487 deletions

View File

@@ -0,0 +1,3 @@
{
"createsItemId": "neues_rezept_1786731383674"
}

View File

@@ -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);
}
@@ -12037,58 +12047,321 @@ public class EditorApp extends Application {
treeRoot.setExpanded(true);
contentCharNode = new javafx.scene.control.TreeItem<>("Characters");
contentCharNode.setExpanded(false);
refreshContentCharacterTree();
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;
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;" : "");
}
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.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;
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 -> contentCharNode.getChildren().add(
new javafx.scene.control.TreeItem<>(id)));
.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;
}
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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";

View File

@@ -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) {

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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");