diff --git a/build.gradle b/build.gradle index 4cf7c0db8b3..d8f4e4916ce 100644 --- a/build.gradle +++ b/build.gradle @@ -35,13 +35,14 @@ plugins { id "com.dorongold.task-tree" version '2.1.1' // Prints the task dependency tree id 'org.openjfx.javafxplugin' version '0.1.0' // JavaFX support id 'org.beryx.runtime' version '1.13.0' // Creates custom runtimes + id "io.freefair.lombok" version '8.4' // Adding Lombok support } /** * Set the version and the modules we want from JavaFX (not everything) */ javafx { - version = "17" + version = "21" modules = [ 'javafx.controls', 'javafx.web', 'javafx.swing', 'javafx.fxml', 'javafx.graphics' ] /* @@ -271,9 +272,9 @@ dependencies { // https://github.com/TestFX/Monocle/issues/79 testImplementation group: 'org.testfx', name: 'openjfx-monocle', version: 'jdk-12.0.1+2' constraints { - implementation('org.openjfx:javafx-base:17') - implementation('org.openjfx:javafx-controls:17') - implementation('org.openjfx:javafx-graphics:17') { + implementation('org.openjfx:javafx-base:21') + implementation('org.openjfx:javafx-controls:21') + implementation('org.openjfx:javafx-graphics:21') { because 'Monocle uses 12.0.1+2 that does not support Mac`s ARM' } } diff --git a/code/src/java/pcgen/facade/core/CharacterFacade.java b/code/src/java/pcgen/facade/core/CharacterFacade.java index 798d6e6b1a3..d30d84a15fb 100644 --- a/code/src/java/pcgen/facade/core/CharacterFacade.java +++ b/code/src/java/pcgen/facade/core/CharacterFacade.java @@ -23,7 +23,6 @@ import java.io.File; import java.math.BigDecimal; import java.util.List; - import pcgen.cdom.enumeration.CharID; import pcgen.cdom.enumeration.Gender; import pcgen.cdom.enumeration.Handed; @@ -48,6 +47,7 @@ import pcgen.facade.util.ListFacade; import pcgen.facade.util.ReferenceFacade; import pcgen.facade.util.event.ChangeListener; +import pcgen.facade.util.event.ReferenceListener; import pcgen.io.ExportException; import pcgen.io.ExportHandler; @@ -852,4 +852,8 @@ public interface CharacterFacade extends CompanionFacade public String getPreviewSheetVar(String key); public void addPreviewSheetVar(String key, String value); + + public void addStatsBonusesChangedListener(ReferenceListener listener); + + public void removeStatsBonusesChangedListener(ReferenceListener listener); } diff --git a/code/src/java/pcgen/gui2/PCGenFrame.java b/code/src/java/pcgen/gui2/PCGenFrame.java index 684e56e56b9..6422cc7e154 100644 --- a/code/src/java/pcgen/gui2/PCGenFrame.java +++ b/code/src/java/pcgen/gui2/PCGenFrame.java @@ -87,9 +87,9 @@ import pcgen.gui3.GuiUtility; import pcgen.gui3.JFXPanelFromResource; import pcgen.gui3.component.PCGenToolBar; -import pcgen.gui3.dialog.AboutDialog; import pcgen.gui3.dialog.RememberingChoiceDialog; import pcgen.gui3.dialog.TipOfTheDayController; +import pcgen.gui3.dialog.about.AboutDialog; import pcgen.io.PCGFile; import pcgen.persistence.SourceFileLoader; import pcgen.system.CharacterManager; diff --git a/code/src/java/pcgen/gui2/facade/CharacterFacadeImpl.java b/code/src/java/pcgen/gui2/facade/CharacterFacadeImpl.java index 58017c3f13f..d641523baad 100644 --- a/code/src/java/pcgen/gui2/facade/CharacterFacadeImpl.java +++ b/code/src/java/pcgen/gui2/facade/CharacterFacadeImpl.java @@ -35,7 +35,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; - +import org.apache.commons.lang3.StringUtils; import pcgen.cdom.base.AssociatedPrereqObject; import pcgen.cdom.base.CDOMObject; import pcgen.cdom.base.CDOMReference; @@ -149,6 +149,7 @@ import pcgen.facade.util.event.ChangeListener; import pcgen.facade.util.event.ListEvent; import pcgen.facade.util.event.ListListener; +import pcgen.facade.util.event.ReferenceListener; import pcgen.gui2.UIPropertyContext; import pcgen.gui2.util.CoreInterfaceUtilities; import pcgen.io.ExportException; @@ -169,8 +170,6 @@ import pcgen.util.enumeration.Tab; import pcgen.util.enumeration.View; -import org.apache.commons.lang3.StringUtils; - /** * The Class {@code CharacterFacadeImpl} is an implementation of * the {@link CharacterFacade} interface for the new user interface. It is @@ -262,6 +261,7 @@ public class CharacterFacadeImpl private TemplateListener templateListener; private XPListener xpListener; private AutoEquipListener autoEquipListener; + private DefaultReferenceFacade statsBonusesChanged = new DefaultReferenceFacade<>(0); /** * Create a new character facade for an existing character. @@ -460,6 +460,18 @@ private WriteableReferenceFacade getStatReferenceFacade(PCStat stat) return ChannelCompatibility.getStatScore(theCharacter.getCharID(), stat); } + @Override + public void addStatsBonusesChangedListener(ReferenceListener listener) + { + statsBonusesChanged.addReferenceListener(listener); + } + + @Override + public void removeStatsBonusesChangedListener(ReferenceListener listener) + { + statsBonusesChanged.removeReferenceListener(listener); + } + /** * Build up the list of kits that the character has. */ @@ -1598,6 +1610,8 @@ private void refreshStatScores() charLevelsFacade.fireSkillBonusEvent(this, 0, true); charLevelsFacade.updateSkillsTodo(); } + + statsBonusesChanged.set(statsBonusesChanged.get() + 1); } @Override diff --git a/code/src/java/pcgen/gui2/tabs/SummaryInfoTab.java b/code/src/java/pcgen/gui2/tabs/SummaryInfoTab.java index 99efab00557..f9fddcfd4c5 100644 --- a/code/src/java/pcgen/gui2/tabs/SummaryInfoTab.java +++ b/code/src/java/pcgen/gui2/tabs/SummaryInfoTab.java @@ -30,11 +30,12 @@ import java.awt.GridLayout; import java.awt.Insets; import java.awt.event.ActionEvent; +import java.io.IOException; +import java.net.URL; import java.text.NumberFormat; import java.util.Arrays; import java.util.Collection; import java.util.TreeSet; - import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; @@ -61,7 +62,7 @@ import javax.swing.border.TitledBorder; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; - +import org.apache.commons.lang3.StringUtils; import pcgen.cdom.enumeration.Gender; import pcgen.cdom.enumeration.Handed; import pcgen.cdom.util.CControl; @@ -103,11 +104,16 @@ import pcgen.gui2.util.SimpleTextIcon; import pcgen.gui3.JFXPanelFromResource; import pcgen.gui3.SimpleHtmlPanelController; +import pcgen.gui3.tabs.summary.AbilityScores; +import pcgen.gui3.tabs.summary.AbilityScoresModel; import pcgen.gui3.utilty.ColorUtilty; import pcgen.system.LanguageBundle; import pcgen.util.enumeration.Tab; -import org.apache.commons.lang3.StringUtils; +import javafx.application.Platform; +import javafx.embed.swing.JFXPanel; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; /** * This component displays a basic summary of a character such as name, @@ -120,7 +126,8 @@ public class SummaryInfoTab extends JPanel implements CharacterInfoTab, TodoHand private final TabTitle tabTitle; private final JPanel basicsPanel; private final JPanel todoPanel; - private final JPanel scoresPanel; + private final JFXPanel scoresPanel; + private AbilityScores abilityScores; private final JPanel racePanel; private final JPanel classPanel; private final JTextField characterNameField; @@ -169,7 +176,7 @@ public class SummaryInfoTab extends JPanel implements CharacterInfoTab, TodoHand this.tabTitle = new TabTitle(Tab.SUMMARY); this.basicsPanel = new JPanel(); this.todoPanel = new JPanel(); - this.scoresPanel = new JPanel(); + this.scoresPanel = new JFXPanel(); this.racePanel = new JPanel(); this.classPanel = new JPanel(); this.characterNameField = new JTextField(); @@ -245,7 +252,8 @@ private void initComponents() gbc.gridheight = GridBagConstraints.REMAINDER; add(todoPanel, gbc); - initMiddlePanel(scoresPanel); + Platform.runLater(() -> initMiddlePanel(scoresPanel)); + gbc.gridy = GridBagConstraints.RELATIVE; gbc.weightx = 1; add(scoresPanel, gbc); @@ -257,6 +265,8 @@ private void initComponents() gbc.weightx = 0.1; gbc.weighty = 1; add(rightPanel, gbc); + + } private static void setPanelTitle(JComponent panel, String title) @@ -284,15 +294,34 @@ private void initTodoPanel(JPanel panel) panel.add(scroll, BorderLayout.CENTER); } - private void initMiddlePanel(JPanel middlePanel) + private void initMiddlePanel(JFXPanel middlePanel) { + URL resource = AbilityScores.class.getResource(AbilityScores.class.getSimpleName() + ".fxml"); + FXMLLoader loader = new FXMLLoader(resource, LanguageBundle.getBundle()); + + Scene scene = null; + try + { + scene = loader.load(); + abilityScores = loader.getController(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + + middlePanel.setScene(scene); + + StatTableModel.initializeTable(statsTable); + + middlePanel.setLayout(new GridLayout(2, 1)); JPanel statsPanel = new JPanel(); setPanelTitle(statsPanel, LanguageBundle.getString("in_sumAbilityScores")); //$NON-NLS-1$ statsPanel.setLayout(new BoxLayout(statsPanel, BoxLayout.Y_AXIS)); - StatTableModel.initializeTable(statsTable); + JScrollPane pane = new JScrollPane(statsTable, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER) { @@ -333,11 +362,12 @@ public Dimension getMaximumSize() statTotalPanel.add(Box.createHorizontalGlue()); statsPanel.add(statTotalPanel); - middlePanel.add(statsPanel); + //middlePanel.add(statsPanel); pane = new JScrollPane(infoPane); setPanelTitle(pane, LanguageBundle.getString("in_sumStats")); //$NON-NLS-1$ - middlePanel.add(pane); + //middlePanel.add(pane); + } private void initRightPanel(JPanel rightPanel) @@ -665,6 +695,8 @@ public ModelMap createModels(final CharacterFacade character) models.put(ExpSubtractAction.class, new ExpSubtractAction(character)); models.put(TodoListHandler.class, new TodoListHandler(character)); models.put(HPHandler.class, new HPHandler(character)); + //TODO: new + models.put(AbilityScoresModel.class, new AbilityScoresModel(character)); return models; } @@ -689,6 +721,9 @@ public void storeModels(ModelMap models) models.get(HPHandler.class).uninstall(); models.get(ComboBoxRendererHandler.class).uninstall(); + + //TODO: new + models.get(AbilityScoresModel.class).uninstall(); } @Override @@ -722,6 +757,11 @@ public void restoreModels(ModelMap models) expsubtractButton.setAction(models.get(ExpSubtractAction.class)); addLevelsAction.install(); + //TODO: new + AbilityScoresModel abilityScoresModel = models.get(AbilityScoresModel.class); + abilityScoresModel.install(); + abilityScores.getAbilityScoresModelProperty().set(abilityScoresModel); + resetBasicsPanel(); } diff --git a/code/src/java/pcgen/gui3/dialog/AboutDialog.java b/code/src/java/pcgen/gui3/dialog/about/AboutDialog.java similarity index 98% rename from code/src/java/pcgen/gui3/dialog/AboutDialog.java rename to code/src/java/pcgen/gui3/dialog/about/AboutDialog.java index d0fa7bafad2..60141439a40 100644 --- a/code/src/java/pcgen/gui3/dialog/AboutDialog.java +++ b/code/src/java/pcgen/gui3/dialog/about/AboutDialog.java @@ -16,11 +16,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package pcgen.gui3.dialog; +package pcgen.gui3.dialog.about; import java.io.IOException; import java.util.concurrent.CompletableFuture; - import pcgen.gui3.Controllable; import pcgen.gui3.GuiAssertions; import pcgen.system.LanguageBundle; diff --git a/code/src/java/pcgen/gui3/dialog/AboutDialogController.java b/code/src/java/pcgen/gui3/dialog/about/AboutDialogController.java similarity index 99% rename from code/src/java/pcgen/gui3/dialog/AboutDialogController.java rename to code/src/java/pcgen/gui3/dialog/about/AboutDialogController.java index 386050f7425..e0a88f99662 100644 --- a/code/src/java/pcgen/gui3/dialog/AboutDialogController.java +++ b/code/src/java/pcgen/gui3/dialog/about/AboutDialogController.java @@ -15,14 +15,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package pcgen.gui3.dialog; +package pcgen.gui3.dialog.about; import java.io.IOException; import java.net.URI; import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; - +import org.apache.commons.lang3.StringUtils; import pcgen.gui2.tools.DesktopBrowserLauncher; import pcgen.system.LanguageBundle; import pcgen.system.PCGenPropBundle; @@ -34,7 +34,6 @@ import javafx.scene.control.Labeled; import javafx.scene.control.TextArea; import javafx.scene.text.Text; -import org.apache.commons.lang3.StringUtils; /** * Controller for about panel. diff --git a/code/src/java/pcgen/gui3/tabs/summary/AbilityScores.java b/code/src/java/pcgen/gui3/tabs/summary/AbilityScores.java new file mode 100644 index 00000000000..7fcc3ec4691 --- /dev/null +++ b/code/src/java/pcgen/gui3/tabs/summary/AbilityScores.java @@ -0,0 +1,94 @@ +package pcgen.gui3.tabs.summary; + +import lombok.Getter; +import pcgen.gui3.utilty.CustomTableCellFactory; + +import javafx.beans.binding.Bindings; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ObservableIntegerValue; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.cell.TextFieldTableCell; +import javafx.util.Callback; +import javafx.util.converter.IntegerStringConverter; + +@Getter +public class AbilityScores +{ + private final ObjectProperty abilityScoresModelProperty = new SimpleObjectProperty<>(); + + private final ObservableList pcStatList = + FXCollections.observableArrayList();/*statValues-> new Observable[]{ + statValues.getLabel(), + statValues.getTotal(), + statValues.getMod(), + statValues.getBase(), + statValues.getRaceBonus(), + statValues.getOtherBonus() + });*/ + + @FXML + public TableColumn editableColumn; + + public AbilityScores() + { + abilityScoresModelProperty.subscribe((model -> { + if (model != null) + { + AbilityScoresModel previousModel = abilityScoresModelProperty.get(); + if (previousModel != null) + { + Bindings.unbindContentBidirectional(pcStatList, model.getPcStatList()); + } + abilityScoresModelProperty.set(model); + Bindings.bindContentBidirectional(pcStatList, model.getPcStatList()); + assert !pcStatList.isEmpty(); + } + })); + } + + public Callback, ObservableValue> getLabelFactory() + { + return cellDataFeatures -> cellDataFeatures.getValue().getLabel(); + } + + public Callback, ObservableValue> getTotalFactory() + { + return cellDataFeatures -> cellDataFeatures.getValue().getTotal(); + } + + public Callback, ObservableIntegerValue> getModFactory() + { + return cellDataFeatures -> cellDataFeatures.getValue().getMod(); + } + + public Callback, ObservableIntegerValue> getBaseFactory() + { + return cellDataFeatures -> cellDataFeatures.getValue().getBase(); + } + + public Callback, TableCell> getBaseCellFactory() + { + return TextFieldTableCell.forTableColumn(new IntegerStringConverter()); + } + + public Callback, TableCell> getCustomizationCellFactory() + { + return CustomTableCellFactory.forTableColumn(EditableStatCell.class); + } + + public Callback, ObservableIntegerValue> getRaceBonusFactory() + { + return cellDataFeatures -> cellDataFeatures.getValue().getRaceBonus(); + } + + public Callback, ObservableIntegerValue> getOtherBonusFactory() + { + return cellDataFeatures -> cellDataFeatures.getValue().getOtherBonus(); + } +} diff --git a/code/src/java/pcgen/gui3/tabs/summary/AbilityScoresModel.java b/code/src/java/pcgen/gui3/tabs/summary/AbilityScoresModel.java new file mode 100644 index 00000000000..29c21ed933d --- /dev/null +++ b/code/src/java/pcgen/gui3/tabs/summary/AbilityScoresModel.java @@ -0,0 +1,185 @@ +package pcgen.gui3.tabs.summary; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.Value; +import org.jetbrains.annotations.NotNull; +import pcgen.core.PCStat; +import pcgen.facade.core.CharacterFacade; +import pcgen.facade.util.event.ReferenceEvent; +import pcgen.facade.util.event.ReferenceListener; + +import javafx.application.Platform; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.beans.value.ChangeListener; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +@SuppressWarnings("rawtypes") +public class AbilityScoresModel implements ReferenceListener +{ + private final CharacterFacade characterFacade; + @Getter + private final ObservableList pcStatList = FXCollections.observableArrayList(); + + @Value + @EqualsAndHashCode(onlyExplicitlyIncluded = true) // Properties don't implement a sane equals method + public static class StatValues + { + @Builder + private StatValues(@NonNull String label, + int mod, + int base, + int raceBonus, + @NonNull String total, + int otherBonus, + @NonNull ChangeListener changeListener) + { + this.label.set(label); + this.mod.set(mod); + this.base.set(base); + this.raceBonus.set(raceBonus); + this.total.set(total); + this.otherBonus.set(otherBonus); + + this.base.addListener(changeListener); + } + + StringProperty label = new SimpleStringProperty(); + IntegerProperty mod = new SimpleIntegerProperty(); + IntegerProperty base = new SimpleIntegerProperty(); + IntegerProperty raceBonus = new SimpleIntegerProperty(); + StringProperty total = new SimpleStringProperty(); + IntegerProperty otherBonus = new SimpleIntegerProperty(); + + @EqualsAndHashCode.Include + private String getLabelValue() + { + return label.get(); + } + + @EqualsAndHashCode.Include + private int getModValue() + { + return mod.getValue(); + } + + @EqualsAndHashCode.Include + private int getBaseValue() + { + return base.getValue(); + } + + @EqualsAndHashCode.Include + private int getRaceBonusValue() + { + return raceBonus.getValue(); + } + + @EqualsAndHashCode.Include + private String getTotalValue() + { + return total.getValue(); + } + + @EqualsAndHashCode.Include + private int getOtherBonusValue() + { + return otherBonus.getValue(); + } + } + + public AbilityScoresModel(CharacterFacade characterFacade) + { + this.characterFacade = characterFacade; + } + + private List getStats() + { + List result = new ArrayList<>(); + + characterFacade.getDataSet().getStats().forEach(result::add); + + return result; + } + + @SuppressWarnings("unchecked") + public void install() + { + getStats().forEach(stat -> characterFacade.getScoreBaseRef(stat).addReferenceListener((ReferenceListener) this)); + characterFacade.addStatsBonusesChangedListener(this); + rebuildStatList(); + //individual Listeners + } + + @SuppressWarnings("unchecked") + public void uninstall() + { + getStats().forEach(stat -> characterFacade.getScoreBaseRef(stat).removeReferenceListener((ReferenceListener) this)); + characterFacade.addStatsBonusesChangedListener(this); + } + + @Override + @SuppressWarnings("rawtypes") + public void referenceChanged(ReferenceEvent e) + { + // defer rebuilding because the backend is still in the middle of applying the bonuses + Platform.runLater(this::rebuildStatList); + } + + private void rebuildStatList() + { + List stats = getStats(); + + if (pcStatList.size() != stats.size()) + { + // full rebuild; should only happen when the list is still empty - after that the sizes should always be the same + pcStatList.setAll( + stats.stream() + .map(stat -> createObjectProperty(stat, characterFacade)) + .collect(Collectors.toList())); + } + else + { + // only change the properties, which will only raise events if the new values are different + for (int i = 0; i < stats.size(); i++) + { + overrideObjectProperty(stats.get(i), characterFacade, pcStatList.get(i)); + } + } + } + + @NotNull + private static StatValues createObjectProperty(PCStat stat, CharacterFacade character) + { + return StatValues.builder() + .label(stat.getDisplayName()) + .mod(character.getModTotal(stat)) + .base(character.getScoreBase(stat)) + .raceBonus(character.getScoreRaceBonus(stat)) + .total(character.getScoreTotalString(stat)) + .otherBonus(character.getScoreOtherBonus(stat)) + .changeListener((observable, oldValue, newValue) -> + character.setScoreBase(stat, newValue.intValue())) + .build(); + } + + @NotNull + private static void overrideObjectProperty(PCStat stat, CharacterFacade character, StatValues values) + { + values.getLabel().setValue(stat.getDisplayName()); + values.getMod().setValue(character.getModTotal(stat)); + values.getBase().setValue(character.getScoreBase(stat)); + values.getRaceBonus().setValue(character.getScoreRaceBonus(stat)); + values.getTotal().setValue(character.getScoreTotalString(stat)); + values.getOtherBonus().setValue(character.getScoreOtherBonus(stat)); + } +} diff --git a/code/src/java/pcgen/gui3/tabs/summary/EditableStatCell.java b/code/src/java/pcgen/gui3/tabs/summary/EditableStatCell.java new file mode 100644 index 00000000000..013340a9534 --- /dev/null +++ b/code/src/java/pcgen/gui3/tabs/summary/EditableStatCell.java @@ -0,0 +1,43 @@ +package pcgen.gui3.tabs.summary; + +import lombok.Getter; +import pcgen.gui3.utilty.BoundController; + +import javafx.beans.property.Property; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.FXML; +import javafx.scene.control.Spinner; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; + +@Getter +public class EditableStatCell implements BoundController> +{ + private final Property externalProperty = new SimpleObjectProperty<>(); + + @FXML + public Spinner spinner; + + public void initialize(){ + spinner.focusWithinProperty().addListener((observable, oldValue, newValue)->{ + if (oldValue && ! newValue){ + externalProperty.setValue(spinner.getValueFactory().getValue()); + } + }); + + externalProperty.subscribe(value -> spinner.getValueFactory().setValue(value)); + } + + @Override + public void bind(Property property) + { + externalProperty.bindBidirectional(property); // don't bind the spinner directly; only update when we lose focus + } + + public void onKeyReleased(KeyEvent keyEvent) + { + if (KeyCode.ENTER.equals(keyEvent.getCode())){ + externalProperty.setValue(spinner.getValueFactory().getValue()); + } + } +} diff --git a/code/src/java/pcgen/gui3/utilty/BoundController.java b/code/src/java/pcgen/gui3/utilty/BoundController.java new file mode 100644 index 00000000000..d04ff5e47ff --- /dev/null +++ b/code/src/java/pcgen/gui3/utilty/BoundController.java @@ -0,0 +1,8 @@ +package pcgen.gui3.utilty; + +import javafx.beans.property.Property; + +public interface BoundController> +{ + void bind(T property); +} diff --git a/code/src/java/pcgen/gui3/utilty/CustomTableCellFactory.java b/code/src/java/pcgen/gui3/utilty/CustomTableCellFactory.java new file mode 100644 index 00000000000..bb16b0511aa --- /dev/null +++ b/code/src/java/pcgen/gui3/utilty/CustomTableCellFactory.java @@ -0,0 +1,91 @@ +package pcgen.gui3.utilty; + +import java.io.IOException; +import java.net.URL; +import java.util.Objects; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.NonNull; +import pcgen.system.LanguageBundle; + +import javafx.application.Platform; +import javafx.beans.property.Property; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.util.Callback; + +@Builder(access = AccessLevel.PROTECTED) +public class CustomTableCellFactory extends TableCell +{ + private final Class fxmlControllerClass; + private final Property valueProperty = new SimpleObjectProperty<>(); + private Node graphic; + private boolean isExternalUpdate; + + public static Callback, TableCell> + forTableColumn(@NonNull Class>> fxmlControllerClass) + { + return list -> CustomTableCellFactory.builder() + .fxmlControllerClass(fxmlControllerClass) + .build(); + } + + protected void initialize() + { + if (graphic != null) + { + return; + } + try + { + URL resource = fxmlControllerClass.getResource(fxmlControllerClass.getSimpleName() + ".fxml"); + FXMLLoader loader = new FXMLLoader(resource, LanguageBundle.getBundle()); + graphic = loader.load(); + valueProperty.addListener((observable, oldValue, newValue) -> { + if (isExternalUpdate || Objects.equals(oldValue, newValue)) + { + return; + } + Platform.runLater(() -> { + startEdit(); + commitEdit(newValue); + requestFocus(); + }); + }); + loader.>>getController().bind(valueProperty); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + @Override + protected void updateItem(T item, boolean empty) + { + isExternalUpdate = true; + try + { + super.updateItem(item, empty); + + if (empty) + { + setGraphic(null); + } + else + { + initialize(); + + valueProperty.setValue(item); + setGraphic(graphic); + } + } + finally + { + isExternalUpdate = false; + } + } +} diff --git a/code/src/java/pcgen/system/LanguageBundle.java b/code/src/java/pcgen/system/LanguageBundle.java index d06dd7d6cdc..1bb1d961d2d 100644 --- a/code/src/java/pcgen/system/LanguageBundle.java +++ b/code/src/java/pcgen/system/LanguageBundle.java @@ -22,7 +22,6 @@ import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; - import pcgen.util.Logging; /** @@ -182,6 +181,10 @@ else if (d == 0.75d) public static ResourceBundle getBundle() { + if (bundle == null) + { + init(); + } return bundle; } } diff --git a/code/src/resources/pcgen/gui3/dialog/AboutDialog.fxml b/code/src/resources/pcgen/gui3/dialog/about/AboutDialog.fxml similarity index 92% rename from code/src/resources/pcgen/gui3/dialog/AboutDialog.fxml rename to code/src/resources/pcgen/gui3/dialog/about/AboutDialog.fxml index 0b0f9642878..19fd37dff1d 100644 --- a/code/src/resources/pcgen/gui3/dialog/AboutDialog.fxml +++ b/code/src/resources/pcgen/gui3/dialog/about/AboutDialog.fxml @@ -18,15 +18,23 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - - + + + + + + + - + + + + + fx:controller="pcgen.gui3.dialog.about.AboutDialogController"> @@ -134,12 +142,12 @@