This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 48f93998b67544f648852c52a7cd2d0e0313daed Author: Martin Desruisseaux <[email protected]> AuthorDate: Mon Jan 27 14:15:20 2020 +0100 Fix layout of error message. --- .../org/apache/sis/gui/coverage/GridError.java | 76 ++++++++++++++++------ .../java/org/apache/sis/gui/coverage/GridRow.java | 1 + .../java/org/apache/sis/gui/coverage/GridTile.java | 39 +++++++---- .../org/apache/sis/gui/coverage/GridViewSkin.java | 66 ++++++++++++------- .../apache/sis/internal/gui/ExceptionReporter.java | 44 ++++++++----- .../org/apache/sis/internal/gui/Resources.java | 5 ++ .../apache/sis/internal/gui/Resources.properties | 1 + .../sis/internal/gui/Resources_fr.properties | 1 + .../org/apache/sis/util/resources/Vocabulary.java | 5 ++ .../sis/util/resources/Vocabulary.properties | 1 + .../sis/util/resources/Vocabulary_fr.properties | 1 + 11 files changed, 167 insertions(+), 73 deletions(-) diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridError.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridError.java index 269a89d..335c2ce 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridError.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridError.java @@ -17,9 +17,16 @@ package org.apache.sis.gui.coverage; import java.awt.Rectangle; +import javafx.geometry.Pos; +import javafx.event.ActionEvent; +import javafx.scene.control.Button; import javafx.scene.control.Label; +import javafx.scene.layout.TilePane; +import javafx.scene.layout.VBox; import javafx.scene.paint.Color; -import org.apache.sis.util.Classes; +import org.apache.sis.internal.gui.ExceptionReporter; +import org.apache.sis.internal.gui.Resources; +import org.apache.sis.util.resources.Vocabulary; /** @@ -30,7 +37,12 @@ import org.apache.sis.util.Classes; * @since 1.1 * @module */ -final class GridError extends Label { +final class GridError extends VBox { + /** + * The area where to write the error message. + */ + private final Label message; + /** * If we failed to fetch a tile, the tile in error. If more than one tile has an error, * then the tile having the largest intersection with the view area. This visible error @@ -39,6 +51,12 @@ final class GridError extends Label { private GridTile.Error visibleError; /** + * The last error reported. This is usually equal to {@link #visibleError}, except that + * the visible error may be {@code null} while {@code lastError} is never reset to null. + */ + private GridTile.Error lastError; + + /** * The zero-based row and columns indices of the area currently shown in {@link GridView}. * This is updated by {@link GridViewSkin#layoutChildren(double, double, double, double)}. */ @@ -57,7 +75,21 @@ final class GridError extends Label { * Creates a new error control. */ GridError() { - setTextFill(Color.RED); + super(9); + message = new Label(); + message.setTextFill(Color.RED); + final Button details = new Button(Vocabulary.format(Vocabulary.Keys.Details)); + final Button retry = new Button(Vocabulary.format(Vocabulary.Keys.Retry)); + final TilePane buttons = new TilePane(12, 0, details, retry); + message.setLabelFor(buttons); + buttons.setPrefRows(1); + buttons.setPrefColumns(2); + buttons.setAlignment(Pos.CENTER); + details.setMaxWidth(100); // Arbitrary limit, width enough for allowing TilePane to resize. + retry .setMaxWidth(100); + getChildren().addAll(message, buttons); + setAlignment(Pos.CENTER); + details.setOnAction(this::showDetails); } /** @@ -66,38 +98,44 @@ final class GridError extends Label { * @param area zero-based row and columns indices of the area currently shown in {@link GridView}. */ final void initialize(final Rectangle area) { - setVisible(false); viewArea = area; visibleError = null; - updateCount++; + if (++updateCount == 0) { + updateCount = 1; // Paranoiac safety in case we did a cycle over all integer values. + } } /** * Updates this error control with the given status. If this control is already showing an error message, - * it will be updated only if the given status cover a larger view area. + * it will be updated only if the given status cover a larger view area. If this control has been updated, + * then this method returns the zero-based row and column indices of the region in error in the view area. + * Otherwise (if this method did nothing), this method returns {@code null}. * * @param status the candidate error status. + * @return new indices of visible area, or {@code null} if no change. + * This is a direct reference to internal field; do not modify. */ - final boolean update(final GridTile.Error status) { + final Rectangle update(final GridTile.Error status) { if (status != visibleError && status.updateAndCompare(updateCount, viewArea, visibleError)) { - visibleError = status; + visibleError = lastError = status; + String text = status.message; final Throwable exception = status.exception; - String message = exception.getLocalizedMessage(); - if (message == null) { - message = Classes.getShortClassName(exception); + String more = exception.getLocalizedMessage(); + if (more != null) { + text = text + System.lineSeparator() + more; } - setText(message); - setVisible(true); - return true; + message.setText(text); + return status.visibleArea; } - return false; + return null; } /** - * Returns the zero-based row and column indices of the region in error in the view area. - * This method returns a direct reference to internal instance; do not modify. + * Invoked when the user click on the "details" button. */ - final Rectangle getVisibleArea() { - return visibleError.visibleArea; + private void showDetails(final ActionEvent event) { + if (lastError != null) { + ExceptionReporter.show(Resources.format(Resources.Keys.ErrorDataAccess), lastError.message, lastError.exception); + } } } diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridRow.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridRow.java index 56b8bf2..721d2aa 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridRow.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridRow.java @@ -82,6 +82,7 @@ final class GridRow extends IndexedCell<Void> { if (skin != null) { ((GridRowSkin) skin).setRowIndex(row); } + updateItem(null, row < 0 || row >= view.getImageHeight()); } /** diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridTile.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridTile.java index 32b3110..a1fa654 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridTile.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridTile.java @@ -21,6 +21,7 @@ import java.awt.image.Raster; import java.awt.image.RenderedImage; import javafx.concurrent.Task; import org.apache.sis.internal.gui.BackgroundThreads; +import org.apache.sis.internal.gui.Resources; /** @@ -148,7 +149,8 @@ final class GridTile { tile = null; status = null; if (view.getImage() == image) { - status = new Error(view.getTileBounds(tileX, tileY), getException()); + status = new Error(Resources.format(Resources.Keys.CanNotFetchTile_2, tileX, tileY), + view.getTileBounds(tileX, tileY), getException()); view.contentChanged(false); // For rendering the error message. } } @@ -185,7 +187,12 @@ final class GridTile { * in the sense that {@link #load(GridView)} returns {@code null} immediately, except that no * error message is recorded. */ - private static final Error LOADING = new Error(null, null); + private static final Error LOADING = new Error(null, null, null); + + /** + * The error message saying "can not fetch tile (x, y)", with tile indices. + */ + final String message; /** * If we failed to load the tile, the reason for the failure. @@ -201,8 +208,6 @@ final class GridTile { /** * Intersection of {@link #region} with the area currently shown in the view. * May vary with scrolling and is empty if the tile in error is outside visible area. - * - * @see GridError#getVisibleArea() */ Rectangle visibleArea; @@ -215,19 +220,13 @@ final class GridTile { /** * Creates an error status with the given cause. */ - private Error(final Rectangle region, final Throwable exception) { + private Error(final String message, final Rectangle region, final Throwable exception) { + this.message = message; this.region = region; this.exception = exception; } /** - * Returns the area inside {@link #visibleArea}. - */ - private long area() { - return visibleArea.width * (long) visibleArea.height; - } - - /** * Recomputes the {@link #visibleArea} value if needed, then returns {@code true} * if the visible area in this {@code Error} if wider than the one in {@code other}. * @@ -241,7 +240,21 @@ final class GridTile { visibleArea = viewArea.intersection(region); updateCount = stamp; } - return (other == null) || area() > other.area(); + if (other == null || other.visibleArea.isEmpty()) { + return true; + } + if (visibleArea.isEmpty()) { + return false; + } + /* + * Gives precedence to width instead than computing the area because the error + * messsage will be written horizontally, so we want more space for writing it. + */ + int c = Integer.compare(visibleArea.width, other.visibleArea.width); + if (c == 0) { + c = Integer.compare(visibleArea.height, other.visibleArea.height); + } + return c > 0; } } } diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java index b3fb8f9..53d65bb 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java @@ -179,16 +179,30 @@ final class GridViewSkin extends VirtualContainerBase<GridView, GridRow> { if (error == null) { error = new GridError(); getChildren().add(error); + computeErrorBounds((Flow) getVirtualFlow()); } - if (error.update(status)) { - final double cellHeight = getVirtualFlow().getFixedCellSize(); - final java.awt.Rectangle area = error.getVisibleArea(); - positionInArea(error, - area.x * cellWidth + headerWidth, - area.y * cellHeight + topBackground.getHeight(), - area.width * cellWidth, - area.height * cellHeight, - 0, HPos.CENTER, VPos.CENTER); + final java.awt.Rectangle area = error.update(status); + if (area != null) { + final Flow flow = (Flow) getVirtualFlow(); + final GridRow firstVisibleRow = area.isEmpty() ? null : flow.getFirstVisibleCell(); + error.setVisible(firstVisibleRow != null); + if (firstVisibleRow != null) { + final double cellHeight = flow.getFixedCellSize(); + final double width = area.width * cellWidth; + final double height = area.height * cellHeight; + layoutInArea(error, + cellWidth * (area.x - firstVisibleColumn) + leftBackground.getWidth(), + cellHeight * (area.y - firstVisibleRow.getIndex()) + topBackground.getHeight(), + width, height, Node.BASELINE_OFFSET_SAME_AS_HEIGHT, HPos.CENTER, VPos.CENTER); + /* + * If after layout the error message size appears too large for the remaining space, hide it. + * The intent is to avoid having the message and buttons on top of numbers in valid tiles. + * It does not seem to work fully however (some overlaps can still happen). + */ + if (error.getHeight() > height || error.getWidth() > width) { + error.setVisible(false); + } + } } } @@ -395,25 +409,29 @@ final class GridViewSkin extends VirtualContainerBase<GridView, GridRow> { int column = firstVisibleColumn; for (final Node cell : headerRow) { ((GridCell) cell).setText(view.formatHeaderValue(column++, false)); - layoutInArea(cell, pos, y, cellWidth, headerHeight, 0, HPos.CENTER, VPos.CENTER); + positionInArea(cell, pos, y, cellWidth, headerHeight, Node.BASELINE_OFFSET_SAME_AS_HEIGHT, HPos.CENTER, VPos.CENTER); pos += cellWidth; } } - /* - * If an error exists somewhere, computes as estimation of the visible region - * as zero-based column and row indices. We use an AWT rectangle instead than - * JavaFX object because this rectangle will be intersected with AWT rectangle. - */ if (error != null) { - final java.awt.Rectangle viewArea = new java.awt.Rectangle(); - final GridRow firstVisibleRow = flow.getFirstVisibleCell(); - if (firstVisibleRow != null) { - viewArea.x = firstVisibleColumn; - viewArea.y = firstVisibleRow.getIndex(); - viewArea.width = (int) ((flow.getWidth() - headerWidth) / cellWidth); - viewArea.height = (int) (flow.getVisibleHeight() / flow.getFixedCellSize()); - } - error.initialize(viewArea); + computeErrorBounds(flow); + } + } + + /** + * If an error exists somewhere, computes as estimation of the visible region + * as zero-based column and row indices. We use an AWT rectangle instead than + * JavaFX object because this rectangle will be intersected with AWT rectangle. + */ + private void computeErrorBounds(final Flow flow) { + final java.awt.Rectangle viewArea = new java.awt.Rectangle(); + final GridRow firstVisibleRow = flow.getFirstVisibleCell(); + if (firstVisibleRow != null) { + viewArea.x = firstVisibleColumn; + viewArea.y = firstVisibleRow.getIndex(); + viewArea.width = (int) ((flow.getWidth() - leftBackground.getWidth()) / cellWidth); + viewArea.height = (int) (flow.getVisibleHeight() / flow.getFixedCellSize()); } + error.initialize(viewArea); } } diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java index 1c5a143..b11d1ca 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java @@ -167,30 +167,40 @@ public final class ExceptionReporter implements EventHandler<ActionEvent> { * @param title {@link Resources.Keys} of the title, or 0 if unknown. * @param text {@link Resources.Keys} of the text (possibly with arguments), or 0 if unknown. * @param arguments the arguments for creating the text identified by the {@code text} key. - * @param exception the exception to report. + * @param exception the exception to report, or {@code null} if none. */ private static void show(final short title, final short text, final Object[] arguments, final Throwable exception) { if (exception != null) { - String message = exception.getLocalizedMessage(); - if (message == null) { - message = Classes.getShortClassName(exception); - } - final Alert alert = new Alert(Alert.AlertType.ERROR); + String t = null, h = null; if ((title | text) != 0) { final Resources resources = Resources.getInstance(); - if (title != 0) { - alert.setTitle(resources.getString(title)); - } - if (text != 0) { - alert.setHeaderText(resources.getString(text, arguments)); - } + if (title != 0) t = resources.getString(title); + if (text != 0) h = resources.getString(text, arguments); } - alert.setContentText(message); - final DialogPane pane = alert.getDialogPane(); - pane.setExpandableContent(new ExceptionReporter(exception).trace); - pane.setPrefWidth(650); - alert.show(); + show(t, h, exception); + } + } + + /** + * Constructs and shows the exception reporter. + * + * @param title the window the title, or {@code null} if none. + * @param text the text in the dialog box, or {@code null} if none. + * @param exception the exception to report. + */ + public static void show(final String title, final String text, final Throwable exception) { + String message = exception.getLocalizedMessage(); + if (message == null) { + message = Classes.getShortClassName(exception); } + final Alert alert = new Alert(Alert.AlertType.ERROR); + if (title != null) alert.setTitle(title); + if (text != null) alert.setHeaderText(text); + alert.setContentText(message); + final DialogPane pane = alert.getDialogPane(); + pane.setExpandableContent(new ExceptionReporter(exception).trace); + pane.setPrefWidth(650); + alert.show(); } /** diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java index 7f92874..c864ce9 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java @@ -81,6 +81,11 @@ public final class Resources extends IndexedResourceBundle { public static final short CanNotCreateCRS_1 = 35; /** + * Can not fetch tile ({0}, {1}). + */ + public static final short CanNotFetchTile_2 = 45; + + /** * Can not open “{0}”. */ public static final short CanNotReadFile_1 = 5; diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties index e8636a7..db5d34f 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties +++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties @@ -22,6 +22,7 @@ Abstract = Abstract: AllFiles = All files +CanNotFetchTile_2 = Can not fetch tile ({0}, {1}). CanNotReadFile_1 = Can not open \u201c{0}\u201d. CanNotClose_1 = Can not close \u201c{0}\u201d. Data may be lost. CanNotCreateCRS_1 = Can not create reference system \u201c{0}\u201d. diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties index c8e25a5..2699e37 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties +++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties @@ -27,6 +27,7 @@ Abstract = R\u00e9sum\u00e9\u00a0: AllFiles = Tous les fichiers +CanNotFetchTile_2 = Ne peut pas obtenir la tuile ({0}, {1}). CanNotReadFile_1 = Ne peut pas ouvrir \u00ab\u202f{0}\u202f\u00bb. CanNotClose_1 = Ne peut pas fermer \u00ab\u202f{0}\u202f\u00bb. Il pourrait y avoir une perte de donn\u00e9es. CanNotCreateCRS_1 = Ne peut pas cr\u00e9er le syst\u00e8me de r\u00e9f\u00e9rence \u00ab\u202f{0}\u202f\u00bb. diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java index 6307ba9..94f8285 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java +++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java @@ -770,6 +770,11 @@ public final class Vocabulary extends IndexedResourceBundle { public static final short Result = 132; /** + * Retry + */ + public static final short Retry = 189; + + /** * Root */ public static final short Root = 133; diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties index aecdb5b..6d2e80a 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties +++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties @@ -157,6 +157,7 @@ RemoteConfiguration = Remote configuration RepresentativeValue = Representative value Resolution = Resolution Result = Result +Retry = Retry Root = Root RootMeanSquare = Root Mean Square SampleDimensions = Sample dimensions diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties index 0276620..bc1779e 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties +++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties @@ -164,6 +164,7 @@ RemoteConfiguration = Configuration distante RepresentativeValue = Valeur repr\u00e9sentative Resolution = R\u00e9solution Result = R\u00e9sultat +Retry = R\u00e9essayer Root = Racine RootMeanSquare = Moyenne quadratique SampleDimensions = Dimensions d\u2019\u00e9chantillonnage
