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
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 30ef29f Factor out the CRS filter predicate in a separated class. Put
an hourglass icon while loading CRS tabular data.
30ef29f is described below
commit 30ef29fd5ff571abc0a8c30c788c8ec8b95b336a
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue Nov 12 22:19:01 2019 +0100
Factor out the CRS filter predicate in a separated class.
Put an hourglass icon while loading CRS tabular data.
---
.../apache/sis/gui/referencing/AuthorityCodes.java | 33 +++++++
.../org/apache/sis/gui/referencing/CRSChooser.java | 59 ++---------
.../org/apache/sis/gui/referencing/CodeFilter.java | 110 +++++++++++++++++++++
3 files changed, 150 insertions(+), 52 deletions(-)
diff --git
a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/AuthorityCodes.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/AuthorityCodes.java
index 0f05141..2909596 100644
---
a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/AuthorityCodes.java
+++
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/AuthorityCodes.java
@@ -29,6 +29,7 @@ import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableListBase;
import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
import javafx.concurrent.Task;
import javafx.util.Callback;
import org.opengis.util.FactoryException;
@@ -70,6 +71,12 @@ final class AuthorityCodes extends ObservableListBase<Code>
private static final long REFRESH_DELAY =
StandardDateFormat.NANOS_PER_SECOND / 10;
/**
+ * The table view which use this list, or {@code null} if we don't need
this information anymore.
+ * See {@link #describedCodes} for an explanation about its purpose.
+ */
+ TableView<Code> owner;
+
+ /**
* The type of object for which we want authority codes. Fixed to {@link
CoordinateReferenceSystem} for now,
* but could be made configurable in a future version. Making this field
configurable would require resolving
* the "todo" documented in class javadoc.
@@ -83,6 +90,13 @@ final class AuthorityCodes extends ObservableListBase<Code>
private Object[] codes;
/**
+ * Count of the number of {@linkplain #codes} for which we completed the
{@link Code#name} information.
+ * This is used for notifying the {@linkplain #owner} when we do not
expect more information to be loaded.
+ * This notification is only indicative and may not be fully accurate.
Effect should be only visual.
+ */
+ private int describedCodes;
+
+ /**
* The preferred locale of CRS descriptions.
*/
final Locale locale;
@@ -206,12 +220,30 @@ final class AuthorityCodes extends
ObservableListBase<Code>
final String name = updated.remove(value);
if (name != null) {
((Code) value).name().set(name); // The name
needs to be set in JavaFX thread.
+ describedCodes++;
nextUpdate(i);
}
}
}
nextAdd(s, n);
endChange();
+ if (describedCodes >= n) {
+ removeHourglass();
+ }
+ }
+
+ /**
+ * Removes the hourglass icon which was shown in the table during initial
data loading phase.
+ * Removing this icon restores the JavaFX default behavior, which is to
show "no data" when the
+ * list is empty. We want this default behavior when we think that there
is no more data to load.
+ * This is especially important when the user apply a filter which
produces an empty result.
+ * Since the effect is only visual, its okay if the criterion for invoking
this method is approximate.
+ */
+ private void removeHourglass() {
+ if (owner != null) {
+ owner.setPlaceholder(null);
+ owner = null;
+ }
}
/**
@@ -404,6 +436,7 @@ final class AuthorityCodes extends ObservableListBase<Code>
add(code);
}
error = e;
+ removeHourglass();
scheduleNewLoader();
}
diff --git
a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
index 52ee27d..bdb4e92 100644
---
a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
+++
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
@@ -18,7 +18,6 @@ package org.apache.sis.gui.referencing;
import java.util.Locale;
import java.util.Optional;
-import java.util.function.Predicate;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
@@ -37,6 +36,7 @@ import javafx.scene.control.ToggleButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
+import javafx.scene.text.Font;
import javafx.stage.Window;
import org.apache.sis.internal.gui.ExceptionReporter;
import org.opengis.util.FactoryException;
@@ -44,9 +44,7 @@ import org.opengis.referencing.crs.CRSAuthorityFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.internal.gui.IdentityValueFactory;
import org.apache.sis.internal.gui.Resources;
-import org.apache.sis.internal.util.Strings;
import org.apache.sis.util.resources.Vocabulary;
-import org.apache.sis.util.CharSequences;
import org.apache.sis.referencing.CRS;
@@ -107,6 +105,7 @@ public class CRSChooser extends
Dialog<CoordinateReferenceSystem> {
final Vocabulary vocabulary = Vocabulary.getResources(locale);
final AuthorityCodes codeList = new AuthorityCodes(factory, locale);
table = new TableView<>(codeList);
+ codeList.owner = table;
/*
* Columns to show in CRS table. First column is typically EPSG codes
and second
* column is the CRS descriptions. The content is loaded in a
background thread.
@@ -121,13 +120,16 @@ public class CRSChooser extends
Dialog<CoordinateReferenceSystem> {
table.setPrefWidth(500);
table.getColumns().setAll(codes, names);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
+ final Label clock = new Label("\u23F3"); // Unicode U+23F3:
Hourglass With Flowing Sand.
+ clock.setFont(Font.font(40));
+ table.setPlaceholder(clock);
/*
* Text field for filtering the list of CRS codes using keywords.
* The filtering is applied when the "Enter" key is pressed in that
field.
*/
searchField = new TextField();
searchField.setOnAction((ActionEvent event) -> {
- filter(searchField.getText());
+ CodeFilter.apply(table, searchField.getText());
});
HBox.setHgrow(searchField, Priority.ALWAYS);
final Label label = new Label(i18n.getString(Resources.Keys.Filter));
@@ -136,7 +138,7 @@ public class CRSChooser extends
Dialog<CoordinateReferenceSystem> {
* Button for showing the CRS description in Well Known Text (WKT)
format.
* The button is enabled only if a row in the table is selected.
*/
- final ToggleButton info = new ToggleButton("ℹ"); // Unicode
U+2139: Information Source.
+ final ToggleButton info = new ToggleButton("\uD83D\uDDB9"); // Unicode
U+1F5B9: Document With Text.
table.getSelectionModel().selectedItemProperty().addListener((e,o,n)
-> info.setDisable(n == null));
info.setOnAction((ActionEvent event) -> {
setTools(info.isSelected());
@@ -210,53 +212,6 @@ public class CRSChooser extends
Dialog<CoordinateReferenceSystem> {
}
/**
- * Displays only the CRS whose names contains the specified keywords. The
{@code keywords}
- * argument is a space-separated list provided by the user after he
pressed "Enter" key.
- *
- * @param keywords space-separated list of keywords to look for.
- */
- private void filter(String keywords) {
- final ObservableList<Code> items = table.getItems();
- final AuthorityCodes allCodes;
- FilteredList<Code> filtered;
- if (items instanceof AuthorityCodes) {
- allCodes = (AuthorityCodes) items;
- filtered = null;
- } else {
- filtered = (FilteredList<Code>) items;
- allCodes = (AuthorityCodes) filtered.getSource();
- }
- keywords = Strings.trimOrNull(keywords);
- if (keywords != null) {
- keywords = keywords.toLowerCase(allCodes.locale);
- final String[] tokens = (String[]) CharSequences.split(keywords, '
');
- if (tokens.length != 0) {
- final Predicate<Code> p = (code) -> {
- String name = allCodes.getName(code).getValue();
- if (name == null) {
- return false;
- }
- name = name.toLowerCase(allCodes.locale);
- for (final String token : tokens) {
- if (!name.contains(token)) {
- return false;
- }
- }
- return true;
- };
- if (filtered == null) {
- filtered = new FilteredList<>(allCodes, p);
- table.setItems(filtered);
- } else {
- filtered.setPredicate(p);
- }
- return;
- }
- }
- table.setItems(allCodes);
- }
-
- /**
* Returns the currently selected CRS, or {@code null} if none.
*
* @return the currently selected CRS, or {@code null}.
diff --git
a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CodeFilter.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CodeFilter.java
new file mode 100644
index 0000000..59dcfab
--- /dev/null
+++
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CodeFilter.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.gui.referencing;
+
+import java.util.function.Predicate;
+import javafx.collections.ObservableList;
+import javafx.collections.transformation.FilteredList;
+import javafx.scene.control.TableView;
+import org.apache.sis.internal.util.Strings;
+import org.apache.sis.util.CharSequences;
+
+
+/**
+ * Filters the list of CRS codes based on keywords.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+final class CodeFilter implements Predicate<Code> {
+ /**
+ * The list of codes to filter.
+ */
+ private final AuthorityCodes allCodes;
+
+ /**
+ * Keywords that must be present in filtered CRS.
+ */
+ private final String[] tokens;
+
+ /**
+ * Creates a new filter.
+ */
+ private CodeFilter(final AuthorityCodes allCodes, final String[] tokens) {
+ this.allCodes = allCodes;
+ this.tokens = tokens;
+ }
+
+ /**
+ * Displays only the CRS whose names contains the specified keywords. The
{@code keywords}
+ * argument is a space-separated list provided by the user after he
pressed "Enter" key in
+ * {@link CRSChooser#searchField}.
+ *
+ * @param table the table on which to apply filtering.
+ * @param keywords space-separated list of keywords to look for.
+ */
+ static void apply(final TableView<Code> table, String keywords) {
+ final ObservableList<Code> items = table.getItems();
+ final AuthorityCodes allCodes;
+ FilteredList<Code> filtered;
+ if (items instanceof AuthorityCodes) {
+ allCodes = (AuthorityCodes) items;
+ filtered = null;
+ } else {
+ filtered = (FilteredList<Code>) items;
+ allCodes = (AuthorityCodes) filtered.getSource();
+ }
+ keywords = Strings.trimOrNull(keywords);
+ if (keywords != null) {
+ keywords = keywords.toLowerCase(allCodes.locale);
+ final String[] tokens = (String[]) CharSequences.split(keywords, '
');
+ if (tokens.length != 0) {
+ final Predicate<Code> p = new CodeFilter(allCodes, tokens);
+ if (filtered == null) {
+ filtered = new FilteredList<>(allCodes, p);
+ table.setItems(filtered);
+ } else {
+ filtered.setPredicate(p);
+ }
+ return;
+ }
+ }
+ table.setItems(allCodes);
+ }
+
+ /**
+ * Returns {@code true} if the given code should be included in the
filtered list.
+ * This method is invoked by {@link FilteredList}.
+ */
+ @Override
+ public boolean test(final Code code) {
+ String name = allCodes.getName(code).getValue();
+ if (name == null) {
+ return false;
+ }
+ name = name.toLowerCase(allCodes.locale);
+ for (final String token : tokens) {
+ if (!name.contains(token)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}