mirror of
https://github.com/frosch95/SmartCSV.fx.git
synced 2026-04-11 21:48:22 +02:00
removed the error list and added a clickable sidaebar wih error markers to have more editing space
This commit is contained in:
@@ -43,7 +43,7 @@ import ninja.javafx.smartcsv.FileWriter;
|
||||
import ninja.javafx.smartcsv.csv.CSVFileReader;
|
||||
import ninja.javafx.smartcsv.csv.CSVFileWriter;
|
||||
import ninja.javafx.smartcsv.fx.about.AboutController;
|
||||
import ninja.javafx.smartcsv.fx.list.ValidationErrorListCell;
|
||||
import ninja.javafx.smartcsv.fx.list.ErrorSideBar;
|
||||
import ninja.javafx.smartcsv.fx.preferences.PreferencesController;
|
||||
import ninja.javafx.smartcsv.fx.table.ObservableMapValueFactory;
|
||||
import ninja.javafx.smartcsv.fx.table.ValidationCellFactory;
|
||||
@@ -132,9 +132,6 @@ public class SmartCSVController extends FXMLController {
|
||||
@FXML
|
||||
private Label stateName;
|
||||
|
||||
@FXML
|
||||
private ListView errorList;
|
||||
|
||||
@FXML
|
||||
private AnchorPane tableWrapper;
|
||||
|
||||
@@ -162,6 +159,8 @@ public class SmartCSVController extends FXMLController {
|
||||
@FXML
|
||||
private Button addRowButton;
|
||||
|
||||
private ErrorSideBar errorSideBar;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// members
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -185,8 +184,13 @@ public class SmartCSVController extends FXMLController {
|
||||
this.resourceBundle = resourceBundle;
|
||||
|
||||
setupTableCellFactory();
|
||||
setupErrorListCellFactory();
|
||||
setupErrorListSelectionListener();
|
||||
|
||||
errorSideBar = new ErrorSideBar(resourceBundle);
|
||||
|
||||
errorSideBar.selectedValidationErrorProperty().addListener((observable, oldValue, newValue) -> {
|
||||
scrollToError(newValue);
|
||||
});
|
||||
applicationPane.setRight(errorSideBar);
|
||||
|
||||
bindMenuItemsToCsvFileExtistence(saveMenuItem, saveAsMenuItem, addRowMenuItem);
|
||||
bindButtonsToCsvFileExistence(saveButton, saveAsButton, addRowButton);
|
||||
@@ -196,14 +200,6 @@ public class SmartCSVController extends FXMLController {
|
||||
loadCsvPreferencesFromFile();
|
||||
}
|
||||
|
||||
private void setupErrorListSelectionListener() {
|
||||
errorList.getSelectionModel().selectedItemProperty().addListener(observable -> scrollToError());
|
||||
}
|
||||
|
||||
private void setupErrorListCellFactory() {
|
||||
errorList.setCellFactory(param -> new ValidationErrorListCell(resourceBundle));
|
||||
}
|
||||
|
||||
private void setupTableCellFactory() {
|
||||
cellFactory = new ValidationCellFactory(resourceBundle);
|
||||
}
|
||||
@@ -510,8 +506,7 @@ public class SmartCSVController extends FXMLController {
|
||||
setLeftAnchor(tableView, 0.0);
|
||||
setRightAnchor(tableView, 0.0);
|
||||
tableWrapper.getChildren().setAll(tableView);
|
||||
|
||||
errorList.setItems(model.getValidationError());
|
||||
errorSideBar.setModel(model);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,8 +535,7 @@ public class SmartCSVController extends FXMLController {
|
||||
tableView.getColumns().add(column);
|
||||
}
|
||||
|
||||
private void scrollToError() {
|
||||
ValidationError entry = (ValidationError)errorList.getSelectionModel().getSelectedItem();
|
||||
private void scrollToError(ValidationError entry) {
|
||||
if (entry != null) {
|
||||
if (entry.getLineNumber() != null) {
|
||||
tableView.scrollTo(max(0, entry.getLineNumber() - 1));
|
||||
|
||||
201
src/main/java/ninja/javafx/smartcsv/fx/list/ErrorSideBar.java
Normal file
201
src/main/java/ninja/javafx/smartcsv/fx/list/ErrorSideBar.java
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2015 javafx.ninja <info@javafx.ninja>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
package ninja.javafx.smartcsv.fx.list;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.WeakListChangeListener;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.canvas.Canvas;
|
||||
import javafx.scene.canvas.GraphicsContext;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Text;
|
||||
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
|
||||
import ninja.javafx.smartcsv.validation.ValidationError;
|
||||
import org.controlsfx.control.PopOver;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
|
||||
|
||||
/**
|
||||
* clickable side bar with error markers
|
||||
*/
|
||||
public class ErrorSideBar extends Pane {
|
||||
|
||||
private ListChangeListener<ValidationError> errorListListener = c -> requestLayout();
|
||||
private WeakListChangeListener<ValidationError> weakErrorListListener = new WeakListChangeListener<>(errorListListener);
|
||||
private ObjectProperty<CSVModel> model = new SimpleObjectProperty<>();
|
||||
private Canvas canvas = new Canvas();
|
||||
private ObjectProperty<ValidationError> selectedValidationError = new SimpleObjectProperty<>();
|
||||
private PopOver popOver = new PopOver();
|
||||
private static final double WIDTH = 20.0;
|
||||
|
||||
public ErrorSideBar(ResourceBundle resourceBundle) {
|
||||
getChildren().add(canvas);
|
||||
setFixWidth();
|
||||
configurePopOver();
|
||||
addModelListener();
|
||||
addMouseClickListener();
|
||||
addOnMouseOverListenerForPopOver(resourceBundle);
|
||||
}
|
||||
|
||||
public void setModel(CSVModel model) {
|
||||
this.model.set(model);
|
||||
}
|
||||
|
||||
public CSVModel getModel() {
|
||||
return model.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<CSVModel> modelProperty() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public ValidationError getSelectedValidationError() {
|
||||
return selectedValidationError.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<ValidationError> selectedValidationErrorProperty() {
|
||||
return selectedValidationError;
|
||||
}
|
||||
|
||||
public void setSelectedValidationError(ValidationError selectedValidationError) {
|
||||
this.selectedValidationError.set(selectedValidationError);
|
||||
}
|
||||
|
||||
private void addOnMouseOverListenerForPopOver(ResourceBundle resourceBundle) {
|
||||
setOnMouseMoved(event -> {
|
||||
List<ValidationError> errors = findValidationErrors(event.getY());
|
||||
if (!errors.isEmpty()) {
|
||||
StringWriter messages = new StringWriter();
|
||||
for (ValidationError validationError: errors) {
|
||||
messages.append(getI18nValidatioMessage(resourceBundle, validationError)).append("\n");
|
||||
}
|
||||
popOver.setContentNode(popupContent(messages.toString()));
|
||||
popOver.show(ErrorSideBar.this.getParent(), event.getScreenX()-WIDTH, event.getScreenY());
|
||||
} else {
|
||||
popOver.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addMouseClickListener() {
|
||||
setOnMouseClicked(event -> {
|
||||
List<ValidationError> errors = findValidationErrors(event.getY());
|
||||
if (!errors.isEmpty()) {
|
||||
selectedValidationError.setValue(errors.get(0));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addModelListener() {
|
||||
model.addListener((observable, oldValue, newValue) -> {
|
||||
newValue.getValidationError().addListener(weakErrorListListener);
|
||||
requestLayout();
|
||||
});
|
||||
}
|
||||
|
||||
private void configurePopOver() {
|
||||
popOver.setArrowLocation(PopOver.ArrowLocation.RIGHT_CENTER);
|
||||
}
|
||||
|
||||
private void setFixWidth() {
|
||||
setMinWidth(WIDTH);
|
||||
setPrefWidth(WIDTH);
|
||||
setMaxWidth(WIDTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layoutChildren() {
|
||||
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) {
|
||||
List<ValidationError> errorList = model.get().getValidationError();
|
||||
if (errorList != null && !errorList.isEmpty()) {
|
||||
int rows = model.get().getRows().size();
|
||||
double space = ((int)canvas.getHeight()) / rows;
|
||||
for (ValidationError error : errorList) {
|
||||
double blockStart = space * error.getLineNumber();
|
||||
if (blockStart-1 <= y && y <= blockStart+3) {
|
||||
errors.add(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
private void drawErrorMarker(int w, int h) {
|
||||
|
||||
GraphicsContext g = canvas.getGraphicsContext2D();
|
||||
g.clearRect(0, 0, w, h);
|
||||
g.setFill(Color.valueOf("#ff8888"));
|
||||
|
||||
if (model.get() != null) {
|
||||
List<ValidationError> errorList = model.get().getValidationError();
|
||||
if (errorList != null && !errorList.isEmpty()) {
|
||||
int rows = model.get().getRows().size();
|
||||
double space = h / rows;
|
||||
for (ValidationError error : errorList) {
|
||||
double blockStart = space * error.getLineNumber();
|
||||
g.fillRect(0, blockStart, w, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Node popupContent(String text) {
|
||||
VBox vBox = new VBox();
|
||||
vBox.setPadding(new Insets(10,10,10,10));
|
||||
vBox.getChildren().add(new Text(text));
|
||||
return vBox;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2015 javafx.ninja <info@javafx.ninja>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
package ninja.javafx.smartcsv.fx.list;
|
||||
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.text.Text;
|
||||
import ninja.javafx.smartcsv.validation.ValidationError;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
|
||||
|
||||
/**
|
||||
* Cell to show the error text
|
||||
*/
|
||||
public class ValidationErrorListCell extends ListCell<ValidationError> {
|
||||
|
||||
private ResourceBundle resourceBundle;
|
||||
|
||||
public ValidationErrorListCell(ResourceBundle resourceBundle) {
|
||||
this.resourceBundle = resourceBundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(ValidationError validationError, boolean empty) {
|
||||
super.updateItem(validationError, empty);
|
||||
if (empty) {
|
||||
clearContent();
|
||||
} else {
|
||||
addContent(validationError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void clearContent() {
|
||||
setText(null);
|
||||
setGraphic(null);
|
||||
}
|
||||
|
||||
private void addContent(ValidationError validationError) {
|
||||
setText(null);
|
||||
Text text = new Text(getI18nValidatioMessage(resourceBundle, validationError));
|
||||
text.setWrappingWidth(180);
|
||||
setGraphic(text);
|
||||
}
|
||||
}
|
||||
@@ -146,3 +146,10 @@
|
||||
-fx-padding: 0 5.417em 0 0;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
-fx-background-radius: 2 2 2 2;
|
||||
-fx-background-color: linear-gradient(#eeeeee, #aaaaaa);
|
||||
-fx-text-fill: black;
|
||||
-fx-font-size: 12px;
|
||||
-fx-padding: 10 10 10 10;
|
||||
}
|
||||
@@ -1,17 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.*?>
|
||||
<?import java.net.*?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import de.jensd.fx.glyphs.fontawesome.*?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.canvas.Canvas?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.control.Menu?>
|
||||
<?import javafx.scene.control.MenuBar?>
|
||||
<?import javafx.scene.control.MenuItem?>
|
||||
<?import javafx.scene.control.SeparatorMenuItem?>
|
||||
<?import javafx.scene.control.SplitPane?>
|
||||
<?import javafx.scene.control.TableView?>
|
||||
<?import javafx.scene.control.ToolBar?>
|
||||
<?import javafx.scene.control.Tooltip?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<BorderPane fx:id="applicationPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8.0.66" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<?import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView?>
|
||||
<?import ninja.javafx.smartcsv.fx.list.ErrorSideBar?>
|
||||
<BorderPane fx:id="applicationPane" maxHeight="-Infinity" maxWidth="1000.0" minHeight="700.0" minWidth="-Infinity" prefHeight="700.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<top>
|
||||
<VBox prefWidth="100.0" BorderPane.alignment="CENTER" id="background">
|
||||
<VBox id="background" prefWidth="100.0" BorderPane.alignment="CENTER">
|
||||
<children>
|
||||
<MenuBar>
|
||||
<menus>
|
||||
@@ -103,7 +119,7 @@
|
||||
<FontAwesomeIconView styleClass="save-icon" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button fx:id="saveAsButton" disable="true" mnemonicParsing="false" onAction="#saveAsCsv" text="..." styleClass="last">
|
||||
<Button fx:id="saveAsButton" disable="true" mnemonicParsing="false" onAction="#saveAsCsv" styleClass="last" text="...">
|
||||
<tooltip>
|
||||
<Tooltip text="%menu.save.as" />
|
||||
</tooltip>
|
||||
@@ -114,7 +130,7 @@
|
||||
</HBox>
|
||||
<Region styleClass="spacer" />
|
||||
<HBox styleClass="segmented-button-bar">
|
||||
<Button fx:id="deleteRowButton" disable="true" mnemonicParsing="false" onAction="#deleteRow" styleClass="first">
|
||||
<Button fx:id="deleteRowButton" disable="true" mnemonicParsing="false" onAction="#deleteRow" styleClass="first">
|
||||
<tooltip>
|
||||
<Tooltip text="%menu.delete.row" />
|
||||
</tooltip>
|
||||
@@ -122,7 +138,7 @@
|
||||
<FontAwesomeIconView styleClass="delete-icon" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button fx:id="addRowButton" disable="true" mnemonicParsing="false" onAction="#addRow" styleClass="last">
|
||||
<Button fx:id="addRowButton" disable="true" mnemonicParsing="false" onAction="#addRow" styleClass="last">
|
||||
<tooltip>
|
||||
<Tooltip text="%menu.add.row" />
|
||||
</tooltip>
|
||||
@@ -155,29 +171,12 @@
|
||||
</VBox>
|
||||
</top>
|
||||
<center>
|
||||
<SplitPane dividerPositions="0.8" prefHeight="160.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
||||
<items>
|
||||
<AnchorPane fx:id="tableWrapper">
|
||||
<TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" BorderPane.alignment="CENTER">
|
||||
<columns>
|
||||
</columns>
|
||||
</TableView>
|
||||
</AnchorPane>
|
||||
<VBox>
|
||||
<children>
|
||||
<Label text="%title.validation.errors">
|
||||
<padding>
|
||||
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
|
||||
</padding>
|
||||
<graphic>
|
||||
<FontAwesomeIconView styleClass="error-title-icon" />
|
||||
</graphic>
|
||||
</Label>
|
||||
<ListView fx:id="errorList" minWidth="10.0" prefWidth="200.0" VBox.vgrow="ALWAYS" />
|
||||
</children>
|
||||
</VBox>
|
||||
</items>
|
||||
</SplitPane>
|
||||
<AnchorPane fx:id="tableWrapper">
|
||||
<TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" BorderPane.alignment="CENTER">
|
||||
<columns>
|
||||
</columns>
|
||||
</TableView>
|
||||
</AnchorPane>
|
||||
</center>
|
||||
<left>
|
||||
</left>
|
||||
|
||||
Reference in New Issue
Block a user