package com.twasyl.slideshowfx.controls; import com.twasyl.slideshowfx.engine.presentation.configuration.PresentationConfiguration; import com.twasyl.slideshowfx.utils.beans.Pair; import de.jensd.fx.glyphs.GlyphsStack; import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon; import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView; import javafx.event.EventHandler; import javafx.scene.control.*; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * This class represents a panel used to insert and define variables that can be used within a presentation. * * @author Thierry Wasylczenko * @version 1.0 * @since SlideshowFX 1.0 */ public class PresentationVariablesPanel extends BorderPane { private PresentationConfiguration configuration; private final VBox variablesBox = new VBox(5); private final ScrollPane variablesScrollPane = new ScrollPane(this.variablesBox); private final ToggleGroup variableGroup = new ToggleGroup(); public PresentationVariablesPanel(final PresentationConfiguration configuration) { this.configuration = configuration; this.variablesScrollPane.setPrefSize(500, 500); final FontAwesomeIconView backgroundIcon = new FontAwesomeIconView(FontAwesomeIcon.PLUS_SQUARE); backgroundIcon.setGlyphSize(20); backgroundIcon.setGlyphStyle("-fx-fill: white"); final FontAwesomeIconView plusIcon = new FontAwesomeIconView(FontAwesomeIcon.PLUS); plusIcon.setGlyphSize(15); plusIcon.setGlyphStyle("-fx-fill: app-color-orange"); final GlyphsStack stack = new GlyphsStack(); stack.add(backgroundIcon).add(plusIcon); final Button addButton = new Button(); addButton.getStyleClass().add("image"); addButton.setGraphic(stack); addButton.setTooltip(new Tooltip("Add a variable")); addButton.setOnAction(event -> this.addVariable(null, null)); final ToolBar toolbar = new ToolBar(); toolbar.getItems().add(addButton); this.setTop(toolbar); this.setCenter(this.variablesScrollPane); this.configuration.getVariables().forEach(variable -> this.addVariable(variable.getKey(), variable.getValue())); } /** * This methods add the graphical components to this panel that allows to define a new variable to the configuration. */ private void addVariable(String name, String value) { final Pair<String, String> variable = new Pair<>(); if(name != null) variable.setKey(name); if(value != null) variable.setValue(value); final RadioButton radioButton = new RadioButton(); radioButton.setToggleGroup(this.variableGroup); radioButton.setSelected(true); radioButton.setUserData(variable); final EventHandler<KeyEvent> addVariableByKeyboard = event -> { if (event.isShortcutDown() && KeyCode.ENTER.equals(event.getCode())) { event.consume(); this.addVariable(null, null); } }; final TextField variableName = new TextField(); variableName.setPromptText("Variable's name"); variableName.setPrefColumnCount(10); variableName.textProperty().bindBidirectional(variable.keyProperty()); variableName.setOnKeyPressed(addVariableByKeyboard); final TextField variableValue = new TextField(); variableValue.setPromptText("Variable's value"); variableValue.setPrefColumnCount(15); variableValue.textProperty().bindBidirectional(variable.valueProperty()); variableValue.setOnKeyPressed(addVariableByKeyboard); final FontAwesomeIconView icon = new FontAwesomeIconView(FontAwesomeIcon.TIMES_CIRCLE); icon.setGlyphSize(20); icon.setGlyphStyle("-fx-fill: app-color-orange"); final Button delete = new Button(); delete.setGraphic(icon); delete.setTooltip(new Tooltip("Delete this variable")); final HBox box = new HBox(5); box.getChildren().addAll(radioButton, variableName, variableValue, delete); delete.setOnAction(event -> { this.variablesBox.getChildren().remove(box); this.variableGroup.getToggles().remove(radioButton); }); this.variablesBox.getChildren().add(box); } /** * Get the property that has been selected in this panel. If no selection has been made, {@code null} * is returned. * Moreover, if the name of the property is null or empty, {@code null} will also be returned. * @return The selected property. */ public Pair<String, String> getSelectedVariable() { final RadioButton selection = (RadioButton) this.variableGroup.getSelectedToggle(); if(selection != null) { final Pair<String, String> variable = (Pair<String, String>) selection.getUserData(); if(variable.getKey() == null || variable.getKey().trim().isEmpty()) return null; else { this.normalizeVariable(variable); return variable; } } else return null; } /** * Get the list of valid defined variables. A valid variable is when the name of this variable is * neither {@code null} nor empty. * The list is obtained by all pairs displayed in the component. * * @return The list of valid defined variables or an empty list if no variables are defined or valid. */ public List<Pair<String, String>> getVariables() { final List<Pair<String, String>> variables = new ArrayList<>(); variables.addAll(this.variableGroup.getToggles().stream() .map(toggle -> (Pair<String, String>) toggle.getUserData()) .filter(data -> data.getKey() != null && !data.getKey().trim().isEmpty()) .collect(Collectors.toList())); variables.forEach(this::normalizeVariable); return variables; } /** * Normalize a variable, meaning that if it's value is {@code null} set it to an empty {@link String}. * @param variable The variable to normalize. */ private void normalizeVariable(final Pair<String, String> variable) { if(variable.getValue() == null) { variable.setValue(""); } } }