mirror of
https://github.com/frosch95/SmartCSV.fx.git
synced 2026-04-11 13:38:23 +02:00
use regions instead of drawing into canvas. thx to Dirk Lemmermann
This commit is contained in:
@@ -31,16 +31,10 @@ import javafx.beans.property.SimpleObjectProperty;
|
|||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.WeakListChangeListener;
|
import javafx.collections.WeakListChangeListener;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.canvas.Canvas;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.canvas.GraphicsContext;
|
|
||||||
import javafx.scene.input.MouseEvent;
|
|
||||||
import javafx.scene.layout.Pane;
|
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.paint.Color;
|
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.util.Duration;
|
|
||||||
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
|
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
|
||||||
import ninja.javafx.smartcsv.validation.ValidationError;
|
import ninja.javafx.smartcsv.validation.ValidationError;
|
||||||
import org.controlsfx.control.PopOver;
|
import org.controlsfx.control.PopOver;
|
||||||
@@ -49,31 +43,30 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
import static javafx.geometry.Pos.CENTER;
|
||||||
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
|
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clickable side bar with error markers
|
* clickable side bar with error markers
|
||||||
*/
|
*/
|
||||||
public class ErrorSideBar extends Pane {
|
public class ErrorSideBar extends Region {
|
||||||
|
|
||||||
private static final double WIDTH = 20.0;
|
private static final double WIDTH = 20.0;
|
||||||
private static final int BLOCKSIZE = 6;
|
|
||||||
|
|
||||||
private ListChangeListener<ValidationError> errorListListener = c -> requestLayout();
|
private ListChangeListener<ValidationError> errorListListener = c -> {setErrorMarker();};
|
||||||
private WeakListChangeListener<ValidationError> weakErrorListListener = new WeakListChangeListener<>(errorListListener);
|
private WeakListChangeListener<ValidationError> weakErrorListListener = new WeakListChangeListener<>(errorListListener);
|
||||||
private ObjectProperty<CSVModel> model = new SimpleObjectProperty<>();
|
private ObjectProperty<CSVModel> model = new SimpleObjectProperty<>();
|
||||||
private Canvas canvas = new Canvas();
|
|
||||||
private ObjectProperty<ValidationError> selectedValidationError = new SimpleObjectProperty<>();
|
private ObjectProperty<ValidationError> selectedValidationError = new SimpleObjectProperty<>();
|
||||||
private PopOver popOver = new PopOver();
|
private PopOver popOver = new PopOver();
|
||||||
private List<ValidationError> lastPopupErrors = null;
|
private ResourceBundle resourceBundle;
|
||||||
|
|
||||||
public ErrorSideBar(ResourceBundle resourceBundle) {
|
public ErrorSideBar(ResourceBundle resourceBundle) {
|
||||||
getChildren().add(canvas);
|
this.resourceBundle = resourceBundle;
|
||||||
setFixWidth();
|
setFixWidth();
|
||||||
configurePopOver();
|
|
||||||
addModelListener();
|
addModelListener();
|
||||||
addMouseClickListener();
|
popOver.setAutoHide(true);
|
||||||
addOnMouseOverListenerForPopOver(resourceBundle);
|
popOver.setArrowLocation(PopOver.ArrowLocation.RIGHT_CENTER);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setModel(CSVModel model) {
|
public void setModel(CSVModel model) {
|
||||||
@@ -100,120 +93,53 @@ public class ErrorSideBar extends Pane {
|
|||||||
this.selectedValidationError.set(selectedValidationError);
|
this.selectedValidationError.set(selectedValidationError);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addOnMouseOverListenerForPopOver(ResourceBundle resourceBundle) {
|
|
||||||
setOnMouseMoved(event -> showPopOver(event, resourceBundle));
|
|
||||||
setOnMouseEntered(event -> showPopOver(event, resourceBundle));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addMouseClickListener() {
|
|
||||||
setOnMouseClicked(event -> {
|
|
||||||
List<ValidationError> errors = findValidationErrors(event.getY());
|
|
||||||
if (!errors.isEmpty()) {
|
|
||||||
selectedValidationError.setValue(errors.get(0));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addModelListener() {
|
private void addModelListener() {
|
||||||
model.addListener((observable, oldValue, newValue) -> {
|
model.addListener((observable, oldValue, newValue) -> {
|
||||||
newValue.getValidationError().addListener(weakErrorListListener);
|
newValue.getValidationError().addListener(weakErrorListListener);
|
||||||
requestLayout();
|
setErrorMarker();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showPopOver(MouseEvent event, ResourceBundle resourceBundle) {
|
|
||||||
List<ValidationError> errors = findValidationErrors(event.getY());
|
|
||||||
if (!errors.isEmpty()) {
|
|
||||||
if (!areErrorsAlreadyInPopup(errors)) {
|
|
||||||
lastPopupErrors = errors;
|
|
||||||
popOver.setContentNode(popupContent(getI18nValidatioMessage(resourceBundle, errors)));
|
|
||||||
popOver.show(ErrorSideBar.this.getParent(), event.getScreenX() - WIDTH, event.getScreenY());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lastPopupErrors = null;
|
|
||||||
popOver.hide(Duration.millis(50));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean areErrorsAlreadyInPopup(List<ValidationError> errors) {
|
|
||||||
return lastPopupErrors != null && lastPopupErrors.size() == errors.size() && lastPopupErrors.containsAll(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configurePopOver() {
|
|
||||||
popOver.setArrowLocation(PopOver.ArrowLocation.RIGHT_CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setFixWidth() {
|
private void setFixWidth() {
|
||||||
setMinWidth(WIDTH);
|
setMinWidth(WIDTH);
|
||||||
setPrefWidth(WIDTH);
|
setPrefWidth(WIDTH);
|
||||||
setMaxWidth(WIDTH);
|
setMaxWidth(WIDTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void setErrorMarker() {
|
||||||
protected void layoutChildren() {
|
List<Region> errorMarkerList = new ArrayList<>();
|
||||||
int top = (int) snappedTopInset();
|
|
||||||
int right = (int) snappedRightInset();
|
|
||||||
int bottom = (int) snappedBottomInset();
|
|
||||||
int left = (int) snappedLeftInset();
|
|
||||||
int w = (int) getWidth() - left - right;
|
|
||||||
int h = (int) getHeight() - top - bottom;
|
|
||||||
canvas.setLayoutX(left);
|
|
||||||
canvas.setLayoutY(top);
|
|
||||||
if (w != canvas.getWidth() || h != canvas.getHeight()) {
|
|
||||||
canvas.setWidth(w);
|
|
||||||
canvas.setHeight(h);
|
|
||||||
}
|
|
||||||
drawErrorMarker(w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ValidationError> findValidationErrors(double y) {
|
|
||||||
List<ValidationError> errors = new ArrayList<>();
|
|
||||||
if (model.get() != null) {
|
if (model.get() != null) {
|
||||||
List<ValidationError> errorList = model.get().getValidationError();
|
List<ValidationError> errorList = model.get().getValidationError();
|
||||||
if (errorList != null && !errorList.isEmpty()) {
|
if (errorList != null && !errorList.isEmpty()) {
|
||||||
int rows = model.get().getRows().size();
|
int rows = model.get().getRows().size();
|
||||||
double space = ((int)canvas.getHeight()) / rows;
|
double space = ((int)getHeight()) / rows;
|
||||||
for (ValidationError error : errorList) {
|
for (ValidationError error : errorList) {
|
||||||
double blockStart = space * error.getLineNumber();
|
errorMarkerList.add(generateErrorMarker(space, error));
|
||||||
if (blockStart - 1 <= y && y <= blockStart + BLOCKSIZE + 1) {
|
|
||||||
errors.add(error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
getChildren().setAll(errorMarkerList);
|
||||||
return errors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawErrorMarker(int w, int h) {
|
private Region generateErrorMarker(double space, ValidationError error) {
|
||||||
|
Region errorMarker = new Region();
|
||||||
GraphicsContext g = canvas.getGraphicsContext2D();
|
errorMarker.setLayoutY(space * error.getLineNumber());
|
||||||
g.clearRect(0, 0, w, h);
|
errorMarker.setPrefSize(WIDTH, 2);
|
||||||
g.setFill(Color.valueOf("#ff8888"));
|
errorMarker.setStyle("-fx-background-color: #ff8888");
|
||||||
|
errorMarker.setOnMouseClicked(event -> selectedValidationError.setValue(error));
|
||||||
if (model.get() != null) {
|
errorMarker.setOnMouseEntered(event -> {
|
||||||
List<ValidationError> errorList = model.get().getValidationError();
|
popOver.setContentNode(popupContent(getI18nValidatioMessage(resourceBundle, error)));
|
||||||
if (errorList != null && !errorList.isEmpty()) {
|
popOver.show(errorMarker, -16);
|
||||||
int rows = model.get().getRows().size();
|
});
|
||||||
double space = h / rows;
|
return errorMarker;
|
||||||
for (ValidationError error : errorList) {
|
|
||||||
double blockStart = space * error.getLineNumber();
|
|
||||||
g.fillRect(0, blockStart, w, BLOCKSIZE - 2);
|
|
||||||
}
|
}
|
||||||
for (ValidationError error : errorList) {
|
|
||||||
double blockStart = space * error.getLineNumber();
|
|
||||||
g.clearRect(0, blockStart + BLOCKSIZE + 1, w, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Node popupContent(String text) {
|
private Node popupContent(String text) {
|
||||||
VBox vBox = new VBox();
|
VBox vBox = new VBox();
|
||||||
vBox.setPadding(new Insets(10,10,10,10));
|
vBox.setPadding(new Insets(10,10,10,10));
|
||||||
vBox.getChildren().add(new Text(text));
|
vBox.getChildren().add(new Text(text));
|
||||||
vBox.setAlignment(Pos.CENTER);
|
vBox.setAlignment(CENTER);
|
||||||
return vBox;
|
return vBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user