sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Factor out the CRS filter predicate in a separated class. Put an hourglass icon while loading CRS tabular data.
Date Tue, 12 Nov 2019 21:20:18 GMT
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 <martin.desruisseaux@geomatys.com>
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;
+    }
+}


Mime
View raw message