de.rpgframework.jfx.section.AppearanceSection Maven / Gradle / Ivy
package de.rpgframework.jfx.section;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.nio.file.Files;
import java.util.ResourceBundle;
import java.util.prefs.Preferences;
import org.prelle.javafx.JavaFXConstants;
import org.prelle.javafx.ResponsiveControl;
import org.prelle.javafx.ResponsiveControlManager;
import org.prelle.javafx.Section;
import org.prelle.javafx.SymbolIcon;
import org.prelle.javafx.TitledComponent;
import org.prelle.javafx.WindowMode;
import de.rpgframework.ResourceI18N;
import de.rpgframework.character.RuleSpecificCharacterObject;
import de.rpgframework.core.BabylonEventBus;
import de.rpgframework.core.BabylonEventType;
import de.rpgframework.genericrpg.chargen.CharacterController;
import de.rpgframework.jfx.Constants;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
/**
* @author prelle
*
*/
public class AppearanceSection extends Section implements ResponsiveControl {
private final static Logger logger = System.getLogger(AppearanceSection.class.getPackageName());
private final static ResourceBundle RES = ResourceBundle.getBundle(HistoryElementSection.class.getPackageName()+".Section");
private static Preferences CONFIG = Preferences.userRoot().node(Constants.LAST_OPEN_DIR);
private Button btnPortraitEdit;
private Button btnPortraitRemove;
public ImageView iView;
private TextField tfColorHair;
private TextField tfColorSkin;
private TextField tfColorEyes;
private TextField tfSize;
private TextField tfWeight;
private TextField tfBirthday;
private RuleSpecificCharacterObject,?,?,?> model;
private Image dummyImage;
//-------------------------------------------------------------------
public AppearanceSection() {
this( null );
}
//-------------------------------------------------------------------
@SuppressWarnings("rawtypes")
public AppearanceSection(RuleSpecificCharacterObject model) {
super(ResourceI18N.get(RES, "section.appearance.title"), null);
setId("appearance");
initComponents();
if (ResponsiveControlManager.getCurrentMode()==WindowMode.MINIMAL) {
toMinimal();
} else {
toNonMinimal();
}
this.model = model;
refresh();
initInteractivity();
}
//-------------------------------------------------------------------
private void initComponents() {
btnPortraitEdit = new Button(null, new SymbolIcon("Edit"));
btnPortraitEdit.setTooltip(new Tooltip(ResourceI18N.get(RES, "section.appearance.button.edit")));
btnPortraitRemove = new Button(null, new SymbolIcon("delete"));
btnPortraitRemove.setTooltip(new Tooltip(ResourceI18N.get(RES, "section.appearance.button.delete")));
getButtons().addAll(btnPortraitEdit, btnPortraitRemove);
iView = new ImageView();
iView.setFitWidth(200);
iView.setFitHeight(200);
iView.setStyle("-fx-background-color: rgba(128,0,0,0.5);");
InputStream is = getClass().getResourceAsStream("Placeholder.png");
Image img = new Image(is);
logger.log(Level.WARNING,"AppearanceSection: Exception of "+img+" is "+img.getException());
iView.setImage(img);
tfColorHair = new TextField();
tfColorHair.setPrefColumnCount(8);
tfColorHair.setMinWidth(70);
tfColorSkin = new TextField();
tfColorSkin.setPrefColumnCount(8);
tfColorEyes = new TextField();
tfColorEyes.setPrefColumnCount(8);
tfBirthday = new TextField();
tfBirthday.setPrefColumnCount(8);
tfSize = new TextField();
tfSize.setPrefColumnCount(4);
tfWeight = new TextField();
tfWeight.setPrefColumnCount(4);
}
//-------------------------------------------------------------------
private void toMinimal() {
logger.log(Level.WARNING, "toMinimal");
FlowPane input = new FlowPane(
new TitledComponent(ResourceI18N.get(RES, "section.appearance.label.colorHair"), tfColorHair),
new TitledComponent(ResourceI18N.get(RES, "section.appearance.label.colorSkin"), tfColorSkin),
new TitledComponent(ResourceI18N.get(RES, "section.appearance.label.size"), tfSize),
new TitledComponent(ResourceI18N.get(RES, "section.appearance.label.colorEyes"), tfColorEyes),
new TitledComponent(ResourceI18N.get(RES, "section.appearance.label.birthday"), tfBirthday),
new TitledComponent(ResourceI18N.get(RES, "section.appearance.label.weight"), tfWeight)
);
input.setVgap(5);
input.setHgap(5);
// input.setPrefWrapLength(355);
// input.heightProperty().addListener( (ov,o,n) -> {
// logger.log(Level.INFO, "AppearanceSection.height: "+n);
// AppearanceSection.this.requestLayout();
// AppearanceSection.this.requestParentLayout();
// super.requestLayout();
// });
// input.widthProperty().addListener( (ov,o,n) -> {
//// logger.log(Level.WARNING, "AppearanceSection.width: "+n);
// AppearanceSection.this.requestLayout();
// AppearanceSection.this.requestParentLayout();
// super.requestLayout();
// });
VBox layout = new VBox(5, input, iView);
layout.setAlignment(Pos.TOP_CENTER);
setContent(layout);
logger.log(Level.WARNING, "Call requestLayout() on "+this);
super.requestLayout();
}
//-------------------------------------------------------------------
private void toNonMinimal() {
logger.log(Level.WARNING, "toNonMinimal");
GridPane input = new GridPane();
input.maxWidthProperty().bind(this.widthProperty());
input.setVgap(5);
input.setHgap(5);
input.getColumnConstraints().add(new ColumnConstraints(80,100,200));
Label lbColorHair = new Label(ResourceI18N.get(RES, "section.appearance.label.colorHair"));
Label lbColorSkin = new Label(ResourceI18N.get(RES, "section.appearance.label.colorSkin"));
Label lbColorEyes = new Label(ResourceI18N.get(RES, "section.appearance.label.colorEyes"));
Label lbBirthday = new Label(ResourceI18N.get(RES, "section.appearance.label.birthday"));
Label lbWeight = new Label(ResourceI18N.get(RES, "section.appearance.label.weight"));
Label lbSize = new Label(ResourceI18N.get(RES, "section.appearance.label.size"));
lbColorHair.getStyleClass().add(JavaFXConstants.STYLE_HEADING5);
lbColorSkin.getStyleClass().add(JavaFXConstants.STYLE_HEADING5);
lbColorEyes.getStyleClass().add(JavaFXConstants.STYLE_HEADING5);
lbBirthday.getStyleClass().add(JavaFXConstants.STYLE_HEADING5);
lbWeight.getStyleClass().add(JavaFXConstants.STYLE_HEADING5);
lbSize.getStyleClass().add(JavaFXConstants.STYLE_HEADING5);
input.add(lbColorHair, 0, 0);
input.add(tfColorHair, 1, 0);
input.add(lbColorSkin, 0, 1);
input.add(tfColorSkin, 1, 1);
input.add(lbColorEyes, 0, 2);
input.add(tfColorEyes, 1, 2);
input.add(lbSize, 0, 3);
input.add(tfSize, 1, 3);
input.add(lbWeight, 0, 4);
input.add(tfWeight, 1, 4);
input.add(lbBirthday, 0, 5);
input.add(tfBirthday, 1, 5);
HBox layout = new HBox(10, input, iView);
HBox.setHgrow(layout, Priority.SOMETIMES);
input.setMaxHeight(Double.MAX_VALUE);
setContent(layout);
super.requestLayout();
}
//-------------------------------------------------------------------
private void initInteractivity() {
tfColorHair.textProperty().addListener( (ov,o,n) -> model.setHairColor(n));
tfColorEyes.textProperty().addListener( (ov,o,n) -> model.setEyeColor(n));
tfColorSkin.textProperty().addListener( (ov,o,n) -> model.setSkinColor(n));
tfSize.textProperty().addListener( (ov,o,n) -> model.setSize(Integer.parseInt(n)));
tfWeight.textProperty().addListener( (ov,o,n) -> model.setWeight(Integer.parseInt(n)));
tfBirthday.textProperty().addListener( (ov,o,n) -> model.setAge(n));
btnPortraitRemove.setOnAction(ev -> {
if (model!=null)
model.setImage(null);
InputStream is = getClass().getResourceAsStream("Placeholder.png");
Image img = new Image(is);
System.err.println("AppearanceSection: "+img.getException());
iView.setImage(img);
});
btnPortraitEdit.setOnAction(ev -> {
onAdd();
});
}
//-------------------------------------------------------------------
/**
* @see org.prelle.javafx.ResponsiveControl#setResponsiveMode(org.prelle.javafx.WindowMode)
*/
@Override
public void setResponsiveMode(WindowMode value) {
// TODO Auto-generated method stub
switch (value) {
case MINIMAL:
toMinimal();
break;
default:
toNonMinimal();
}
}
//-------------------------------------------------------------------
public void refresh() {
if (model!=null) {
tfColorEyes.setText(model.getEyeColor());
tfColorHair.setText(model.getHairColor());
tfColorSkin.setText(model.getSkinColor());
tfSize.setText(String.valueOf(model.getSize()));
tfWeight.setText(String.valueOf(model.getWeight()));
tfBirthday.setText(model.getAge());
if (model.getImage()!=null) {
try {
Image image = new Image(new ByteArrayInputStream(model.getImage()));
if (image.isError()) {
logger.log(Level.WARNING, "Failed loading image from character: "+image.getException());
image.getException().printStackTrace();
}
iView.setImage(image);
} catch (Exception e) {
logger.log(Level.WARNING, "Failed loading image from character: "+e);
}
} else {
iView.setImage(dummyImage);
}
}
}
//-------------------------------------------------------------------
private void onAdd() {
logger.log(Level.DEBUG, "opening image selection dialog");
FileChooser chooser = new FileChooser();
chooser.setTitle(ResourceI18N.get(RES,"section.appearance.filechooser.title"));
String lastDir = CONFIG.get(Constants.PROP_LAST_OPEN_IMAGE_DIR, System.getProperty("user.home"));
File lastDir2 = new File(lastDir);
if (lastDir2.exists())
chooser.setInitialDirectory(lastDir2);
chooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("All", "*.*"),
new FileChooser.ExtensionFilter("JPG", "*.jpg"),
new FileChooser.ExtensionFilter("PNG", "*.png")
);
try {
File selection = chooser.showOpenDialog(new Stage());
if (selection!=null) {
CONFIG.put(Constants.PROP_LAST_OPEN_IMAGE_DIR, selection.getParentFile().getAbsolutePath().toString());
try {
byte[] imgBytes = Files.readAllBytes(selection.toPath());
Image image = new Image(new ByteArrayInputStream(imgBytes));
if (image.isError()) {
logger.log(Level.ERROR, "Error loading image "+selection+": ",image.getException());
BabylonEventBus.fireEvent(BabylonEventType.UI_MESSAGE, 2, image.getException());
return;
}
if (image.getWidth()>400 || image.getHeight()>400) {
String msg = ResourceI18N.format(RES, "section.appearance.error.image_too_large", image.getWidth(), image.getHeight());
logger.log(Level.ERROR, "Image too large: "+msg);
double factorX = (image.getWidth()>400)?(400.0/image.getWidth()):1;
double factorY = (image.getHeight()>400)?(400.0/image.getHeight()):1;
double scale = Math.min(factorX, factorY);
logger.log(Level.ERROR, "Scale by "+scale);
image = resample(image, scale);
logger.log(Level.ERROR, "After "+image.getWidth()+"x"+image.getHeight());
int w = (int)image.getWidth();
int h = (int)image.getHeight();
byte[] buf = new byte[w * h * 4];
image.getPixelReader().getPixels(0, 0, w, h, PixelFormat.getByteBgraInstance(), buf, 0, w*4);
// BabylonEventBus.fireEvent(BabylonEventType.UI_MESSAGE, 2, msg);
// return;
}
iView.setImage(image);
if (model!=null)
model.setImage(imgBytes);
} catch (Exception e) {
logger.log(Level.WARNING, "Failed loading image from "+selection+": "+e,e);
}
}
} catch (Exception e) {
logger.log(Level.ERROR, "Failed opening image dialog: "+e);
BabylonEventBus.fireEvent(BabylonEventType.UI_MESSAGE, 2, e.toString());
}
}
//-------------------------------------------------------------------
private Image resample(Image input, double scaleFactor) {
final int W = (int) input.getWidth();
final int H = (int) input.getHeight();
final double S = scaleFactor;
logger.log(Level.INFO, "Scale image to "+W*S +"x" + H*S);
WritableImage output = new WritableImage(
(int)(W * S)+1,
(int)(H * S)+1
);
PixelReader reader = input.getPixelReader();
PixelWriter writer = output.getPixelWriter();
for (int y = 0; y < H; y++) {
for (int x = 0; x < W; x++) {
final int argb = reader.getArgb(x, y);
for (int dy = 0; dy < S; dy++) {
for (int dx = 0; dx < S; dx++) {
writer.setArgb( ((int)(x * S)) + dx, ((int)(y * S)) + dy, argb);
}
}
}
}
return output;
}
//-------------------------------------------------------------------
@SuppressWarnings("rawtypes")
public void updateController(CharacterController ctrl) {
model = ctrl.getModel();
refresh();
}
//-------------------------------------------------------------------
/**
* @param dummyImage the dummyImage to set
*/
public void setDummyImage(Image dummyImage) {
this.dummyImage = dummyImage;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy