de.rpgframework.jfx.skin.DataItemDetailsPaneSkin Maven / Gradle / Ivy
package de.rpgframework.jfx.skin;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.List;
import java.util.ResourceBundle;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.prelle.javafx.JavaFXConstants;
import org.prelle.javafx.ResponsiveControlManager;
import org.prelle.javafx.WindowMode;
import de.rpgframework.ResourceI18N;
import de.rpgframework.character.RuleSpecificCharacterObject;
import de.rpgframework.genericrpg.data.Choice;
import de.rpgframework.genericrpg.data.ComplexDataItem;
import de.rpgframework.genericrpg.data.ComplexDataItemValue;
import de.rpgframework.genericrpg.data.DataItem;
import de.rpgframework.genericrpg.data.Decision;
import de.rpgframework.genericrpg.data.GenericRPGTools;
import de.rpgframework.genericrpg.modification.DataItemModification;
import de.rpgframework.genericrpg.modification.Modification;
import de.rpgframework.genericrpg.modification.ModificationChoice;
import de.rpgframework.genericrpg.modification.ModifiedObjectType;
import de.rpgframework.genericrpg.requirements.Requirement;
import de.rpgframework.jfx.DataItemDetailsPane;
import de.rpgframework.jfx.DataItemPane;
import de.rpgframework.jfx.GenericDescriptionVBox;
import de.rpgframework.jfx.RPGFrameworkJavaFX;
import de.rpgframework.jfx.cells.ChoiceCell;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.SkinBase;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.util.StringConverter;
/**
* @author prelle
*
*/
public class DataItemDetailsPaneSkin extends SkinBase> {
final static ResourceBundle RES = ResourceBundle.getBundle(DataItemPane.class.getName());
private final static Logger logger = RPGFrameworkJavaFX.logger;
// private List modificationTexts;
// private Map modificationButtons;
private ListView listView;
private GenericDescriptionVBox tfDescription;
private TabPane tabs;
private Tab tbEffects = new Tab(ResourceI18N.get(RES, "tab.effects"));
private Tab tbChoices = new Tab(ResourceI18N.get(RES, "tab.choices"));
private Tab tbDescription = new Tab(ResourceI18N.get(RES, "tab.description"));
private Tab tbCustom1 = new Tab();
private Tab tbCustom2 = new Tab();
//-------------------------------------------------------------------
/**
* @param control
*/
public DataItemDetailsPaneSkin(DataItemDetailsPane control, Function reqR, Function modR) {
super(control);
listView = new ListView();
listView.setCellFactory(lv -> new ChoiceCell(new StringConverter() {
public String toString(Choice value) {
if (getSkinnable().getChoiceConverter()!=null)
return getSkinnable().getChoiceConverter().apply(value);
else
return String.valueOf(value);
}
public Choice fromString(String string) {return null;}
},
() -> getSkinnable().getModel(),
(choice) -> {
// ToDo: Open dialog to let user decide
// Choice = choice
// ComplexDataItem = getSkinnable().getSelectedItem()
RPGFrameworkJavaFX.logger.log(Level.WARNING, "getSkinnable(): "+getSkinnable());
getSkinnable().getDecisionHandler().accept(getSkinnable().getSelectedItem(), choice);
}));
tfDescription = new GenericDescriptionVBox(reqR,modR);
tfDescription.showModificationsInDescriptionProperty().bind(control.showModificationsInDescriptionProperty());
tbEffects.setClosable(false);
tbChoices.setClosable(false);
tbDescription.setClosable(false);
tbCustom1.setClosable(false);
tbCustom2.setClosable(false);
initInteractivity();
refresh(ResponsiveControlManager.getCurrentMode());
selectionChanged(control.getSelectedItem());
updateTabs();
}
//-------------------------------------------------------------------
private void initInteractivity() {
getSkinnable().layoutModeProperty().addListener( (ov,o,n) -> refresh(n));
getSkinnable().selectedItemProperty().addListener( (ov,o,n) -> {
logger.log(Level.ERROR, "selection of {1} changed to {0}",n, getSkinnable());
selectionChanged(n);
switch (ResponsiveControlManager.getCurrentMode()) {
case EXPANDED:
refreshExpanded();
break;
default:
updateTabs();
}
});
getSkinnable().modelProperty().addListener( (ov,o,n) -> {
// The character has changed. The decision cells must be updated
listView.refresh();
});
getSkinnable().showHelpForProperty().addListener( (ov,o,n) -> tfDescription.setData(n));
}
//-------------------------------------------------------------------
private void selectionChanged(T n) {
RPGFrameworkJavaFX.logger.log(Level.DEBUG, "selectionChanged: {0}",n);
// Stats
Function modConv = getSkinnable().getModificationConverter();
if (n instanceof ComplexDataItem) {
// Choices
listView.setItems(FXCollections.observableArrayList( ((ComplexDataItem)n).getChoices()));
} else {
listView.getItems().clear();
}
// Description
// tfDescription.getChildren().clear();
// if (n!=null)
// RPGFrameworkJavaFX.parseMarkupAndFillTextFlow(tfDescription, n.getDescription(Locale.getDefault()));
tfDescription.setData(n);
refresh(ResponsiveControlManager.getCurrentMode());
}
//-------------------------------------------------------------------
private void refresh(WindowMode mode) {
RPGFrameworkJavaFX.logger.log(Level.DEBUG, "mode changed to {0}",mode);
switch (mode) {
case EXPANDED:
refreshExpanded();
break;
default:
refreshAsTabs();
}
}
//-------------------------------------------------------------------
private void refreshExpanded() {
listView.setStyle("-fx-min-width: 15em;");
listView.setMaxHeight(Double.MAX_VALUE);
// // Build a label of all modification names
// Label contentStats = new Label(String.join("\n", modificationTexts));
// contentStats.setWrapText(true);
Node contentStats = getContentStats();
// Stats
VBox column1 = new VBox(20);
column1.setStyle("-fx-min-width: 15em; -fx-max-width: 20em");
column1.getStyleClass().addAll("detail-card","item-details-stats");
Label lbStats = new Label(ResourceI18N.get(RES, "tab.effects"));
lbStats.getStyleClass().add(JavaFXConstants.STYLE_HEADING3);
ScrollPane scrollStats = new ScrollPane(contentStats);
scrollStats.setFitToWidth(true);
VBox.setVgrow(scrollStats, Priority.ALWAYS);
column1.getChildren().addAll(lbStats, scrollStats);
// Choices
VBox column2 = new VBox(20);
// column2.setStyle("-fx-min-width: 15em; -fx-background-color:brown");
column2.getStyleClass().addAll("detail-card","item-detail-choices");
Label lbChoices = new Label(ResourceI18N.get(RES, "tab.choices"));
lbChoices.getStyleClass().add(JavaFXConstants.STYLE_HEADING3);
column2.getChildren().addAll(lbChoices, listView);
// Description
Label lbDescr = new Label(ResourceI18N.get(RES, "tab.description"));
lbDescr.getStyleClass().add(JavaFXConstants.STYLE_HEADING3);
tfDescription.setMaxHeight(Double.MAX_VALUE);
VBox column3 = new VBox(0);
column3.getStyleClass().addAll("detail-card","item-detail-description");
column3.getChildren().addAll(lbDescr, tfDescription);
VBox.setVgrow(tfDescription, Priority.ALWAYS);
column3.setMaxHeight(Double.MAX_VALUE);
// Main layout
HBox columns = new HBox(20, column1, column3);
columns.setFillHeight(true);
columns.setMaxHeight(Double.MAX_VALUE);
if (getSkinnable().isShowDecisionColumn()) {
columns.getChildren().add(1, column2);
}
if (!getSkinnable().isShowStatsColumn())
columns.getChildren().remove(column1);
if (listView.getItems().isEmpty() || !getSkinnable().isShowDecisionColumn())
columns.getChildren().remove(column2);
// if (modificationTexts.isEmpty())
// columns.getChildren().remove(column1);
if (getSkinnable().getCustomNode2()!=null) {
// Custom 2
VBox columnC2 = new VBox(20);
columnC2.getStyleClass().add("detail-card");
Label lbCust2 = new Label(getSkinnable().getCustomNode2().getTitle());
lbCust2.getStyleClass().add(JavaFXConstants.STYLE_HEADING3);
columnC2.getChildren().addAll(lbCust2, getSkinnable().getCustomNode2().getContent());
columns.getChildren().add(0, columnC2);
}
if (getSkinnable().getCustomNode1()!=null) {
// Custom 1
VBox columnC1 = new VBox(20);
columnC1.getStyleClass().add("detail-card");
Label lbCust1 = new Label(getSkinnable().getCustomNode1().getTitle());
lbCust1.getStyleClass().add(JavaFXConstants.STYLE_HEADING3);
columnC1.getChildren().addAll(lbCust1, getSkinnable().getCustomNode1().getContent());
columns.getChildren().add(0, columnC1);
}
getChildren().clear();
// columns.setStyle("-fx-background-color: pink");
getChildren().add(columns);
}
//-------------------------------------------------------------------
private void refreshAsTabs() {
Node contentStats = getContentStats();
listView.setMaxHeight(getSkinnable().getHeight()*0.7);
//RPGFrameworkJavaFX.logger.log(Level.WARNING, "Tabs: getSkinnable().height="+getSkinnable().getHeight()+" / "+getSkinnable().getLayoutBounds());
tabs = new TabPane();
tabs.getTabs().addAll(tbEffects, tbDescription);
if (getSkinnable().isShowDecisionColumn()) {
tabs.getTabs().add(1, tbChoices);
}
if (getSkinnable().getScene()!=null)
tabs.setMaxWidth(getSkinnable().getScene().getWidth());
tbEffects.setContent(contentStats);
tbChoices.setContent(listView);
ScrollPane scrollDescr = new ScrollPane(tfDescription);
scrollDescr.setFitToWidth(true);
scrollDescr.setMaxHeight(Double.MAX_VALUE);
tbDescription.setContent(scrollDescr);
if (getSkinnable().getCustomNode2()!=null) {
// Custom 2
tbCustom2.setText(getSkinnable().getCustomNode2().getTitle());
tbCustom2.setContent(getSkinnable().getCustomNode2().getContent());
getSkinnable().getCustomNode2().getContent().setStyle("-fx-padding: 3px;");
tabs.getTabs().add(0, tbCustom2);
}
if (getSkinnable().getCustomNode1()!=null) {
// Custom 1
tbCustom1.setText(getSkinnable().getCustomNode1().getTitle());
tbCustom1.setContent(getSkinnable().getCustomNode1().getContent());
getSkinnable().getCustomNode1().getContent().setStyle("-fx-padding: 3px;");
tabs.getTabs().add(0, tbCustom1);
}
getChildren().clear();
getChildren().add(tabs);
// tabs.setStyle("-fx-border-color: red; -fx-border-width: 3px; -fx-max-width: 40em;");
}
//-------------------------------------------------------------------
private void updateTabs() {
RPGFrameworkJavaFX.logger.log(Level.INFO, "-------------------updateTabs("+getSkinnable().getId()+")--------------------- "+getSkinnable().getSelectedItem());
// Build a label of all modification names
tfDescription.setStyle("-fx-padding: 3px");
tbEffects.setContent(getContentStats());
tbChoices.setContent(listView);
// tbDescription.setContent(new Label(getSkinnable().getSelectedItem().getDescription(Locale.getDefault())));
tbDescription.setContent(tfDescription);
}
//-------------------------------------------------------------------
private Node getContentStats() {
T raw = getSkinnable().getSelectedItem();
VBox contentStats = new VBox();
RPGFrameworkJavaFX.logger.log(Level.WARNING, "getContentStats() for {0} using {1}", raw, getSkinnable().getModificationConverter());
if (raw instanceof ComplexDataItem) {
ComplexDataItem item = (ComplexDataItem)raw;
ModifiedObjectType previousType = null;
Function modConv = getSkinnable().getModificationConverter();
for (Modification m : item.getOutgoingModifications()) {
// Check if it is a new category
if (m.getReferenceType()!=previousType) {
String category = String.valueOf(m.getReferenceType());
if (getSkinnable().getReferenceTypeConverter()!=null && m.getReferenceType()!=null)
category = getSkinnable().getReferenceTypeConverter().apply(m.getReferenceType());
if (category != null) {
Label head = new Label(category);
head.getStyleClass().add(JavaFXConstants.STYLE_HEADING5);
contentStats.getChildren().add(head);
VBox.setMargin(head, new Insets(5, 0, 0, 0));
}
}
previousType = m.getReferenceType();
// Create a (potentially wrapped line) per modification
Button button = null;
Tooltip tooltip = null;
RuleSpecificCharacterObject,?,?,?> model = getSkinnable().getModel();
String text = (modConv!=null)?modConv.apply(m):String.valueOf(m);
Text choiceText = new Text(text);
choiceText.setStyle("-fx-fill: -fx-text-base-color");
Text decText = new Text();
decText.getStyleClass().add(JavaFXConstants.STYLE_HEADING5);
if (m instanceof DataItemModification) {
DataItemModification mod = (DataItemModification)m;
if (mod.getConnectedChoice()!=null) {
if (model.hasDecisionBeenMade(mod.getConnectedChoice())) {
updateChoice(item, mod, decText);
}
Choice choice = item.getChoice(mod.getConnectedChoice());
button = new Button((decText.getText().length()==0)?"?":"!");
button.setOnAction(ev -> {
getSkinnable().getDecisionHandler().accept(getSkinnable().getSelectedItem(), choice);
RPGFrameworkJavaFX.logger.log(Level.WARNING, "Decision dialog closed------------------updating choice");
updateChoice(item, mod, decText);
});
button.setUserData(choice);
} else if (mod.getResolvedKey()!=null && mod.getResolvedKey() instanceof DataItem) {
DataItem toShow = mod.getResolvedKey();
if (toShow!=null) {
tooltip = new Tooltip(toShow.getDescription());
tooltip.setWrapText(true);
tooltip.setMaxWidth(300);
}
}
} else if (m instanceof ModificationChoice) {
ModificationChoice mod = (ModificationChoice)m;
button = new Button((decText.getText().length()==0)?"?":"!");
button.setOnAction(ev -> {
if (getSkinnable().getModDecisionHandler()==null) {
RPGFrameworkJavaFX.logger.log(Level.ERROR, "Missing modDecisionHandler for "+getSkinnable());
} else {
getSkinnable().getModDecisionHandler().accept(getSkinnable().getSelectedItem(), mod);
RPGFrameworkJavaFX.logger.log(Level.WARNING, "Decision dialog closed------------------");
updateChoice(item, mod, decText);
}
});
button.setUserData(mod);
}
TextFlow label = new TextFlow(choiceText, decText);
if (button==null) {
contentStats.getChildren().add(label);
} else {
HBox box = new HBox(5, button, label);
box.setAlignment(Pos.CENTER_LEFT);
contentStats.getChildren().add(box);
}
}
}
return contentStats;
}
//-------------------------------------------------------------------
private void updateChoice(ComplexDataItem item, DataItemModification mod, Text decText) {
RuleSpecificCharacterObject,?,?,?> model = getSkinnable().getModel();
ComplexDataItemValue complex = getSkinnable().getShowChoicesWithValue();
//RPGFrameworkJavaFX.updateChoice(model, complex, getSkinnable().getModificationConverter(), mod, decText);
}
//-------------------------------------------------------------------
private void updateChoice(ComplexDataItem item, ModificationChoice mod, Text decText) {
RuleSpecificCharacterObject,?,?,?> model = getSkinnable().getModel();
Decision dec = model.getDecision(mod.getUUID());
if (dec == null) {
decText.setText("");
} else {
List modList = GenericRPGTools.decisionToModifications(mod, dec);
List modListS = modList.stream().map(mb -> getSkinnable().getModificationConverter().apply(mb))
.collect(Collectors.toList());
decText.setText(" (" + String.join(", ", modListS) + ")");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy