Skip to content

Commit

Permalink
Tool: Changed design of enum editor
Browse files Browse the repository at this point in the history
  • Loading branch information
Gikkman committed Jul 21, 2016
1 parent 21a875f commit d476bc3
Showing 1 changed file with 78 additions and 108 deletions.
@@ -1,84 +1,72 @@
package com.speedment.tool.internal.element;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.animation.AnimationTimer;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import org.controlsfx.property.editor.PropertyEditor;

public final class CommaSeparatedStringEditor extends VBox implements PropertyEditor<String>{
public final class CommaSeparatedStringEditor extends VBox implements PropertyEditor<String> {

//***********************************************************
// VARIABLES
//***********************************************************
//***********************************************************
private final String DEFAULT_FIELD = "ENTER_FIELD_NAME";
private final double SPACING = 10.0;
private final int LIST_HEIGHT = 200;

private final VBox innerBox;
private final SimpleObservable observable;
private final ObservableList<String> strings;
private final StringBinding binding;

//***********************************************************
// CONSTRUCTOR
//***********************************************************
public CommaSeparatedStringEditor(String defaultValue) {
this.observable = new SimpleObservable();
this.binding = Bindings.createStringBinding(() -> getFormatedString(), observable);

//Styling
setSpacing(5.0);

//Add inner VBox, which will hold all rows
innerBox = new VBox();
innerBox.setSpacing(2.0);
getChildren().add(innerBox);

//Add the text generating rows
final Row firstRow = new Row();
innerBox.getChildren().add(firstRow);

//Add the button for adding new rows
final Button addRowButton = new Button("+");
addRowButton.setPrefWidth(50);
addRowButton.setOnAction(v -> {
final Row row = new Row();
innerBox.getChildren().add(row);
observable.notifyListeners();
});
getChildren().add(addRowButton);

this.strings = FXCollections.observableArrayList();
this.binding = Bindings.createStringBinding(() -> getFormatedString(), strings);

setValue(defaultValue);

ListView<String> listView = new ListView<>(strings);
listView.setCellFactory(TextFieldListCell.forListView());
listView.setEditable(true);

listView.setMaxHeight(USE_PREF_SIZE);
listView.setPrefHeight(LIST_HEIGHT);

final HBox controls = new HBox(SPACING);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(addButton(listView), removeButton(listView));

setSpacing(SPACING);
getChildren().addAll(listView, controls);
}

//***********************************************************
// PUBLIC
//***********************************************************
@Override
public ObservableList<Node> getChildren() {
return super.getChildren();
return super.getChildren();
}

public StringBinding binding() {
return binding;
}

public String getFormatedString() {
return innerBox.getChildren().stream()
.filter(Row.class::isInstance)
.map(Row.class::cast)
.map(Row::getText)
.filter( v -> v.length() > 0 )
return strings.stream()
.filter(v -> !v.isEmpty())
.collect(Collectors.joining(","));
}

Expand All @@ -89,84 +77,66 @@ public Node getEditor() {

@Override
public String getValue() {
return getFormatedString();
return getFormatedString();
}

@Override
public void setValue(String value) {
if( value == null ){
innerBox.getChildren().clear();
innerBox.getChildren().add( new Row());
observable.notifyListeners();
if (value == null) {
strings.setAll(DEFAULT_FIELD);
} else {
innerBox.getChildren().setAll(
strings.setAll(
Stream.of(value.split(","))
.map(Row::new)
.toArray(Row[]::new)
.toArray(String[]::new)
);
observable.notifyListeners();
}
}

//***********************************************************
// PRIVATE
//***********************************************************
private final class Row extends HBox {

private final Button removeButton;
private final TextField textField;

Row(){
this(null);
}

Row(String value) {
this.textField = value == null ? new TextField() : new TextField(value);
HBox.setHgrow(textField, Priority.ALWAYS);
this.removeButton = new Button("-");
this.removeButton.setPrefWidth(50);

final Row r = this;
removeButton.setOnAction(v -> {
if (innerBox.getChildren().size() > 1) {
innerBox.getChildren().remove(r);
observable.notifyListeners();
}
});
textField.textProperty().addListener((op, o, n) -> observable.notifyListeners());

setSpacing(8.0);
setAlignment(Pos.CENTER_LEFT);
getChildren().addAll(removeButton, textField);
}


String getText() {
return textField.getText();
}

@Override
public ObservableList<Node> getChildren() {
return super.getChildren();
}
private Button removeButton(final ListView<String> listView) {
final Button button = new Button("Remove Selected");
button.setOnAction(e -> {
final int selectedIdx = listView.getSelectionModel().getSelectedIndex();
if (selectedIdx != -1 && listView.getItems().size() > 1) {
final int newSelectedIdx = (selectedIdx == listView.getItems().size() - 1) ? selectedIdx - 1
: selectedIdx;
listView.getItems().remove(selectedIdx);
listView.getSelectionModel().select(newSelectedIdx);
}
});
return button;
}

private class SimpleObservable implements Observable {

private final Set<InvalidationListener> listeners = Collections.newSetFromMap(new ConcurrentHashMap<>());

@Override
public void addListener(InvalidationListener listener) {
listeners.add(listener);
}

@Override
public void removeListener(InvalidationListener listener) {
listeners.remove(listener);
}

public void notifyListeners() {
listeners.forEach(il -> il.invalidated(this));
}
private Button addButton(final ListView<String> listView) {
final Button button = new Button("Add Item");
button.setOnAction(e -> {
final int newIndex = listView.getItems().size();

listView.getItems().add(DEFAULT_FIELD);
listView.scrollTo(newIndex);
listView.getSelectionModel().select(newIndex);

/* There is a strange behavior in JavaFX if you try to start editing
* a field on the same animation frame as another field lost focus.
*
* Therefore, we wait one animation cycle before setting the field
* into the editing state
*/
new AnimationTimer() {
int frameCount = 0;

@Override
public void handle(long now) {
frameCount++;
if (frameCount > 1) {
listView.edit(newIndex);
stop();
}
}
}.start();
});
return button;
}
}

0 comments on commit d476bc3

Please sign in to comment.