de.rpgframework.jfx.wizard.WizardPageGenerator Maven / Gradle / Ivy
package de.rpgframework.jfx.wizard;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.prelle.javafx.ResponsiveControlManager;
import org.prelle.javafx.SymbolIcon;
import org.prelle.javafx.TitledComponent;
import org.prelle.javafx.WindowMode;
import org.prelle.javafx.Wizard;
import org.prelle.javafx.WizardPage;
import de.rpgframework.ResourceI18N;
import de.rpgframework.character.CharacterHandle;
import de.rpgframework.character.RuleSpecificCharacterObject;
import de.rpgframework.genericrpg.chargen.CharacterGenerator;
import de.rpgframework.genericrpg.chargen.GeneratorId;
import de.rpgframework.genericrpg.chargen.IGeneratorWrapper;
import de.rpgframework.genericrpg.chargen.Rule;
import de.rpgframework.genericrpg.chargen.RuleInterpretation;
import de.rpgframework.genericrpg.data.CommonCharacter;
import de.rpgframework.genericrpg.data.IAttribute;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
import javafx.util.StringConverter;
/**
* @author Stefan
*
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class WizardPageGenerator, G extends CharacterGenerator> extends WizardPage {
private final static Logger logger = System.getLogger(WizardPageGenerator.class.getPackageName());
private static PropertyResourceBundle RES = (PropertyResourceBundle) ResourceBundle.getBundle(WizardPageGenerator.class.getPackageName()+".WizardPages");
private List> allKnown;
private Rule[] allRules;
private IGeneratorWrapper wrapper;
private ChoiceBox cbStrictness;
private ListView> options;
private RuleChoiceBox bxRules;
private Label descHeading;
private Label description;
private VBox bxExtraAndRules;
private VBox descBox;
private VBox layout;
private Function, String[]> nameGetter;
private ObjectProperty extraNode = new SimpleObjectProperty<>();
private Class selected;
//--------------------------------------------------------------------
public WizardPageGenerator(Wizard wizard, IGeneratorWrapper model, List> values,
List interpretations,
Rule[] allRules,
Function, String[]> nameGetter) {
super(wizard);
allKnown = values;
setTitle(ResourceI18N.get(RES, "wizard.generator.title"));
this.wrapper = model;
this.allRules= allRules;
this.nameGetter = nameGetter;
initComponents();
initLayout();
initStyle();
initInteractivity();
// options.getItems().setAll(values);
String expect = Locale.getDefault().getLanguage();
List filtered = interpretations.stream()
.filter( i -> i.getLanguage()==null || expect.equals(i.getLanguage()))
.collect(Collectors.toList());
cbStrictness.getItems().setAll(filtered);
/*
* Try to select current interpretation - otherwise use first
*/
cbStrictness.getSelectionModel().select(0);
if (wrapper!=null && (wrapper.getModel() instanceof de.rpgframework.genericrpg.data.CommonCharacter)) {
String interID = ((CommonCharacter)wrapper.getModel()).getStrictness();
for (RuleInterpretation inter : interpretations) {
if (inter.getId().equals(interID)) {
cbStrictness.getSelectionModel().select(inter);
break;
}
}
}
if (cbStrictness.getValue()!=null)
logger.log(Level.INFO, "Start with strictness '"+cbStrictness.getValue().getId()+"'");
interpretationChanged();
logger.log(Level.INFO, "CURRENT: "+model.getWrapped());
if (!options.getItems().isEmpty()) {
options.getSelectionModel().select((Class) model.getWrapped().getClass());
}
}
//-------------------------------------------------------------------
private void initComponents() {
cbStrictness = new ChoiceBox<>();
cbStrictness.setConverter(new StringConverter() {
public String toString(RuleInterpretation value) { return (value!=null)?value.getName():ResourceI18N.get(RES, "interpretation.label");}
public RuleInterpretation fromString(String arg0) {return null; }
});
options= new ListView>();
options.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
options.setCellFactory(new Callback>, ListCell>>() {
public ListCell> call(ListView> arg0) {
return new CharacterGeneratorListCell(nameGetter);
}
});
// options.getItems().setAll(CharacterGeneratorRegistry.getGenerators());
int lines = Math.max(4, options.getItems().size());
options.setStyle("-fx-pref-width: 15em; -fx-pref-height: "+(lines*3.5)+"em");
bxRules = new RuleChoiceBox(Rule.EffectOn.CHARGEN, Rule.EffectOn.COMMON);
descHeading = new Label();
description = new Label();
description.setWrapText(true);
}
//-------------------------------------------------------------------
private void initLayout() {
descBox = new VBox(20);
descBox.getChildren().addAll(descHeading, description);
description.setStyle("-fx-pref-width: 18em");
layout = new VBox();
// FlowPane content = new FlowPane();
// content.setHgap(20);
// content.setVgap(20);
// content.getChildren().addAll(options, descBox);
super.setContent(layout);
bxExtraAndRules = new VBox(bxRules);
if (extraNode.get()!=null)
bxExtraAndRules.getChildren().add(0,extraNode.get());
// super.setBackContent(new Label("Backcontent"));
super.setBackContent(bxExtraAndRules);
updateLayout();
}
//-------------------------------------------------------------------
private void updateLayout() {
if (logger.isLoggable(Level.TRACE))
logger.log(Level.TRACE, "ENTER updateLayout("+ResponsiveControlManager.getCurrentMode()+")");
logger.log(Level.WARNING, "ENTER updateLayout("+ResponsiveControlManager.getCurrentMode()+")");
layout.getChildren().clear();
bxExtraAndRules.getChildren().retainAll(bxRules);
if (extraNode.get()!=null)
bxExtraAndRules.getChildren().add(0,extraNode.get());
layout.getChildren().addAll(options, descBox);
TitledComponent tcStrict = new TitledComponent(ResourceI18N.get(RES, "wizard.generator.interpretation.label"), cbStrictness);
VBox bxList = new VBox(5, tcStrict, options);
options.setMaxHeight(Double.MAX_VALUE);
bxList.setMaxHeight(Double.MAX_VALUE);
VBox.setVgrow(options, Priority.ALWAYS);
Label lbFineTune = new Label(ResourceI18N.get(RES, "wizard.generator.backheader.finetune"));
Region buf = new Region();
buf.setMaxWidth(Double.MAX_VALUE);
HBox backHeader = new HBox(10, lbFineTune, buf, new SymbolIcon("setting"));
HBox.setHgrow(buf, Priority.ALWAYS);
backHeader.setMaxWidth(Double.MAX_VALUE);
HBox.setMargin(lbFineTune, new Insets(0,0,0,10));
HBox.setMargin(backHeader.getChildren().get(2), new Insets(0,10,0,0));
switch (ResponsiveControlManager.getCurrentMode()) {
case MINIMAL:
super.setBackHeader(backHeader);
VBox helpV = new VBox(20, bxList, descBox);
helpV.setAlignment(Pos.TOP_LEFT);
VBox.setVgrow(bxList, Priority.ALWAYS);
VBox.setVgrow(descBox, Priority.ALWAYS);
bxRules.setStyle("-fx-pref-width: 23em; -fx-min-height: 15em");
bxList.setStyle("-fx-pref-height: 14em");
layout.getChildren().add(helpV);
break;
default:
HBox helpH = new HBox(20, bxList, descBox);
helpH.setMaxHeight(Double.MAX_VALUE);
VBox.setVgrow(helpH, Priority.ALWAYS);
if (ResponsiveControlManager.getCurrentMode()==WindowMode.EXPANDED) {
super.setBackContent(null);
super.setBackHeader(null);
bxRules.setMaxHeight(Double.MAX_VALUE);
bxExtraAndRules.setMaxHeight(Double.MAX_VALUE);
helpH.setFillHeight(true);
helpH.getChildren().add(1, bxExtraAndRules);
HBox.setHgrow(bxRules, Priority.SOMETIMES);
} else {
super.setBackContent(bxExtraAndRules);
super.setBackHeader(backHeader);
}
bxRules.setStyle("-fx-pref-width: 30em; -fx-max-width: 40em");
bxList.setStyle("-fx-pref-height: 20em");
layout.getChildren().add(helpH);
break;
}
if (logger.isLoggable(Level.TRACE))
logger.log(Level.TRACE, "LEAVE updateLayout("+ResponsiveControlManager.getCurrentMode()+")");
}
//-------------------------------------------------------------------
private void initStyle() {
descHeading.getStyleClass().add("text-small-subheader");
description.getStyleClass().add("text-body");
// setImageInsets(new Insets(-40,0,0,0));
}
//-------------------------------------------------------------------
private void initInteractivity() {
options.getSelectionModel().selectedItemProperty().addListener(new ChangeListener>() {
public void changed(ObservableValue extends Class> observable,
Class oldValue, Class newValue) {
if (newValue!=null) {
logger.log(Level.DEBUG, "selected "+newValue);
descHeading.setText(nameGetter.apply(newValue)[0]);
description.setText(nameGetter.apply(newValue)[1]);
} else {
descHeading.setText(null);
description.setText(null);
}
selectGenerator(newValue);
}
});
cbStrictness.getSelectionModel().selectedItemProperty().addListener( (ov,o,n) -> interpretationChanged());
extraNode.addListener( (ov,o,n) -> {
updateLayout();
});
}
// -------------------------------------------------------------------
private void interpretationChanged() {
RuleInterpretation n = cbStrictness.getValue();
try {
// if (logger.isLoggable(Level.TRACE))
logger.log(Level.ERROR, "ENTER: interpretationChanged (" + n + ")");
if (n != null && wrapper != null
&& (wrapper.getModel() instanceof de.rpgframework.genericrpg.data.CommonCharacter)) {
((CommonCharacter) wrapper.getModel()).setStrictness(n.getId());
wrapper.getRuleController().updateEffectiveRules();
}
Class current = options.getSelectionModel().getSelectedItem();
List> toSet = new ArrayList<>(allKnown);
if (wrapper != null) {
// Update list of rule settings
bxRules.setData(wrapper);
// Check is list shall be restricted
if (n!=null && n.getRestrictGenTo() != null) {
logger.log(Level.DEBUG, "CharGens are restricted");
toSet = toSet.stream().filter(r -> {
logger.log(Level.ERROR, "Check {0}",r);
try {
GeneratorId anno = r.getAnnotation(GeneratorId.class);
String id=null;
if (anno==null) {
logger.log(Level.WARNING, r+" is missing @GeneratorId - now I need to instantiate it to obtain the ID");
System.err.println(r+" is missing @GeneratorId - now I need to instantiate it to obtain the ID");
id = r.getDeclaredConstructor().newInstance().getId();
} else {
id = anno.value();
}
logger.log(Level.ERROR, "Is {0} in {1} = {2}",id, n.getRestrictGenTo(),n.getRestrictGenTo().contains(id));
return n.getRestrictGenTo().contains(id);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException | InstantiationException e) {
e.printStackTrace();
return true;
}
}).collect(Collectors.toList());
}
logger.log(Level.ERROR, "Allowed chargens: "+toSet);
options.getItems().setAll(toSet);
}
if (current != null && toSet.contains(current)) {
options.getSelectionModel().select(current);
} else if (!toSet.isEmpty()) {
options.getSelectionModel().select(toSet.get(0));
}
} finally {
if (logger.isLoggable(Level.TRACE))
logger.log(Level.TRACE, "LEAVE: interpretationChanged");
}
}
//-------------------------------------------------------------------
/**
* @see org.prelle.javafx.ResponsiveControl#setResponsiveMode(org.prelle.javafx.WindowMode)
*/
@Override
public void setResponsiveMode(WindowMode value) {
logger.log(Level.WARNING, "ENTER setResponsiveMode(" + value + ")");
updateLayout();
}
//-------------------------------------------------------------------
private void selectGenerator(Class clazz) {
if (logger.isLoggable(Level.TRACE))
logger.log(Level.TRACE, "ENTER selectGenerator(" + clazz + ")");
try {
// Only change if new class is not null
if (clazz == null)
return;
// Find out what generator is currently used - if any
Class currentlyUsed = null;
if (wrapper != null && wrapper.getWrapped() != null) {
currentlyUsed = (Class) wrapper.getWrapped().getClass();
}
logger.log(Level.INFO, "currentlyUsed " + currentlyUsed);
// // Only change something, if it is not already used
// if (currentlyUsed != null && currentlyUsed == clazz) {
// // Is already used - do nothing
// return;
// }
// Create a new instance of the selected generator
logger.log(Level.INFO, "Change generator to " + clazz);
try {
// G newGen = clazz.getConstructor().newInstance();
// wrapper.setWrapped(newGen);
// descHeading.setText(newGen.getName());
// description.setText(newGen.getDescription());
selected = clazz;
} catch (Exception e) {
logger.log(Level.ERROR,"Failed instantiating new generator " + clazz, e);
descHeading.setText(null);
description.setText(null);
}
} finally {
if (logger.isLoggable(Level.TRACE))
logger.log(Level.TRACE, "LEAVE selectGenerator(" + clazz + ")");
}
}
//-------------------------------------------------------------------
/**
* @see org.prelle.javafx.WizardPage#pageVisited()
*/
@Override
public void pageVisited() {
logger.log(Level.DEBUG, "pageVisited: "+wrapper.getWrapped());
logger.log(Level.DEBUG, "selection is "+options.getSelectionModel().getSelectedItem());
// Does the selection require updating?
if (wrapper.getWrapped()!=null && options.getSelectionModel().getSelectedItem()!=wrapper.getWrapped().getClass()) {
options.getSelectionModel().select((Class) wrapper.getWrapped().getClass());
}
}
//-------------------------------------------------------------------
/**
* @see org.prelle.javafx.WizardPage#pageLeft()
*/
@Override
public void pageLeft() {
if (wrapper.getModel() instanceof CommonCharacter) {
logger.log(Level.INFO, "Set chargen used to {0}", wrapper.getId());
((CommonCharacter, ?, ?, ?>)wrapper.getModel()).setCharGenUsed(wrapper.getId());
}
}
//-------------------------------------------------------------------
/**
* @see org.prelle.javafx.WizardPage#beforeLeaving()
*/
@Override
public void beforeLeaving() {
logger.log(Level.DEBUG, "beforeLeaving");
logger.log(Level.DEBUG, "..instance={0}", wrapper.getWrapped());
logger.log(Level.DEBUG, "..selected={0}", selected);
logger.log(Level.DEBUG, "..condition1 {0}", selected!=null);
logger.log(Level.DEBUG, "..condition2 {0}", wrapper.getWrapped().getClass()!=selected);
if (selected!=null && (wrapper.getWrapped()==null || wrapper.getWrapped().getClass()!=selected)) {
logger.log(Level.INFO, "Instantiate {0} (previous {1})",selected, wrapper.getWrapped().getClass());
G newGen = null;
try {
Constructor cons = selected.getConstructor(RuleSpecificCharacterObject.class, CharacterHandle.class);
newGen = cons.newInstance(wrapper.getModel(), null);
} catch (Exception e1) {
logger.log(Level.ERROR, "Expected constructor "+selected.getSimpleName()+"(RulespecificCHaracterObject, CharacterHandle)");
try {
Constructor cons = selected.getConstructor();
newGen = cons.newInstance();
newGen.setModel(wrapper.getModel(), null);
} catch (Exception e) {
logger.log(Level.ERROR, "Problem finding constructor");
e.printStackTrace();
System.exit(1);
}
}
newCharGenCreated(newGen, wrapper.getModel());
try {
// G newGen = selected.getConstructor().newInstance();
wrapper.setWrapped(newGen);
logger.log(Level.DEBUG, "call runProcessors on "+newGen);
newGen.runProcessors();
} catch (Exception e) {
logger.log(Level.ERROR, "Failed instantiating "+selected,e);
} finally {
logger.log(Level.INFO, "Instantiate {0} finished",selected);
}
} else {
wrapper.runProcessors();
}
}
//-------------------------------------------------------------------
/**
* Called when a new character has been created - allows to apply power level
*/
protected void newCharGenCreated(G newGen, C model) {
}
public ObjectProperty extraNode() { return extraNode(); }
public Node getExtraNode() { return extraNode.get(); }
public WizardPageGenerator setExtraNode(Node value) { extraNode.set(value); return this; }
}
class CharacterGeneratorListCell, G extends CharacterGenerator> extends ListCell> {
private final static Logger logger = System.getLogger(CharacterGeneratorListCell.class.getPackageName());
private static PropertyResourceBundle RES = (PropertyResourceBundle) ResourceBundle.getBundle(WizardPageGenerator.class.getPackageName()+".WizardPages");
private Map, G> cache = new HashMap<>();
private VBox box;
private Label lblHeading;
private Label lblHardcopy;
private Function, String[]> nameGetter;
//--------------------------------------------------------------------
public CharacterGeneratorListCell(Function, String[]> nameGetter) {
this.nameGetter = nameGetter;
lblHeading = new Label();
lblHeading.getStyleClass().add("base");
lblHardcopy = new Label();
box = new VBox(5);
box.getChildren().addAll(lblHeading, lblHardcopy);
}
//--------------------------------------------------------------------
/**
* @see javafx.scene.control.Cell#updateItem(java.lang.Object, boolean)
*/
@Override
public void updateItem(Class item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
switch (ResponsiveControlManager.getCurrentMode()) {
case MINIMAL:
setGraphic(lblHeading);
break;
default:
setGraphic(box);
}
try {
if (cache.containsKey(item)) {
G gen = cache.get(item);
lblHeading.setText(gen.getName());
lblHardcopy.setText(gen.getDescription());
} else {
if (logger.isLoggable(Level.TRACE))
logger.log(Level.TRACE, "Need to create instance of Generator to obtain names: "+item);
// G gen = item.getDeclaredConstructor().newInstance();
lblHeading.setText(nameGetter.apply(item)[0]);
// lblHardcopy.setText(gen.getDescription());
// cache.put(item, gen);
}
} catch (Exception e) {
// TODO Auto-generated catch block
logger.log(Level.ERROR, "Failed creating instance of Generator "+item,e);
lblHeading.setText(item.getSimpleName());
}
lblHardcopy.setUserData(item);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy