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;
+    }
+}

Reply via email to