diff --git a/build.gradle b/build.gradle index 0ea7ee5d20..07f9f146cb 100644 --- a/build.gradle +++ b/build.gradle @@ -293,7 +293,7 @@ dependencies { implementation("io.projectreactor.addons:reactor-extra") - def commonsVersion = "f5a7832399" + def commonsVersion = "4a3facc824" implementation("com.github.FAForever.faf-java-commons:faf-commons-data:${commonsVersion}") { exclude module: 'guava' diff --git a/src/main/java/com/faforever/client/domain/ReviewsSummaryBean.java b/src/main/java/com/faforever/client/domain/ReviewsSummaryBean.java index 199e7316fe..acefdda3b8 100644 --- a/src/main/java/com/faforever/client/domain/ReviewsSummaryBean.java +++ b/src/main/java/com/faforever/client/domain/ReviewsSummaryBean.java @@ -17,6 +17,7 @@ public abstract class ReviewsSummaryBean { FloatProperty positive = new SimpleFloatProperty(); FloatProperty negative = new SimpleFloatProperty(); FloatProperty score = new SimpleFloatProperty(); + FloatProperty averageScore = new SimpleFloatProperty(); IntegerProperty numReviews = new SimpleIntegerProperty(); FloatProperty lowerBound = new SimpleFloatProperty(); @@ -64,6 +65,18 @@ public void setScore(float score) { this.score.set(score); } + public FloatProperty averageScoreProperty() { + return averageScore; + } + + public float getAverageScore() { + return averageScore.get(); + } + + public void setAverageScore(float averageScore) { + this.averageScore.set(averageScore); + } + public FloatProperty scoreProperty() { return score; } diff --git a/src/main/java/com/faforever/client/fx/JavaFxUtil.java b/src/main/java/com/faforever/client/fx/JavaFxUtil.java index 6eeb322093..12ca3115f8 100644 --- a/src/main/java/com/faforever/client/fx/JavaFxUtil.java +++ b/src/main/java/com/faforever/client/fx/JavaFxUtil.java @@ -43,8 +43,13 @@ import java.awt.image.BufferedImage; import java.io.IOException; +import java.math.RoundingMode; import java.nio.file.Path; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParseException; import java.util.Arrays; +import java.util.Locale; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -444,6 +449,13 @@ public static void bindManagedToVisible(Node... nodes) { public static void bindTextFieldAndRangeSlider(TextField lowValueTextField, TextField highValueTextField, RangeSlider rangeSlider) { + DecimalFormat numberFormat = (DecimalFormat) DecimalFormat.getInstance(); + numberFormat.setMaximumFractionDigits(0); + bindTextFieldAndRangeSlider(lowValueTextField, highValueTextField, rangeSlider, numberFormat); + } + + public static void bindTextFieldAndRangeSlider(TextField lowValueTextField, TextField highValueTextField, + RangeSlider rangeSlider, DecimalFormat format) { Map.of( lowValueTextField, Pair.of(rangeSlider.lowValueProperty(), rangeSlider.getMin()), highValueTextField, Pair.of(rangeSlider.highValueProperty(), rangeSlider.getMax()) @@ -454,7 +466,7 @@ public static void bindTextFieldAndRangeSlider(TextField lowValueTextField, Text @Override public String toString(Number number) { if (!number.equals(value)) { - return String.valueOf(number.intValue()); + return format.format(number); } else { return ""; } @@ -462,10 +474,14 @@ public String toString(Number number) { @Override public Number fromString(String string) { - if (NumberUtils.isParsable(string)) { - return Double.parseDouble(string); - } else { - if (!string.equals("-") && !string.equals(".")) { + String decimalSeparator = Character.toString(format.getDecimalFormatSymbols().getDecimalSeparator()); + try { + Number number = format.parse(string); + String decimalSeparatorSuffix = string.endsWith(decimalSeparator) && format.getMaximumFractionDigits() > 0 ? decimalSeparator : ""; + textField.setText(format.format(number) + decimalSeparatorSuffix); + return number; + } catch (ParseException e) { + if (!string.equals("-") && !string.equals(decimalSeparator)) { textField.setText(""); } return value; diff --git a/src/main/java/com/faforever/client/map/MapVaultController.java b/src/main/java/com/faforever/client/map/MapVaultController.java index 74609b94c1..4bb3c10878 100644 --- a/src/main/java/com/faforever/client/map/MapVaultController.java +++ b/src/main/java/com/faforever/client/map/MapVaultController.java @@ -33,6 +33,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Random; +import java.util.function.Function; @Slf4j @Component @@ -96,7 +97,8 @@ protected void initSearchController() { searchController.addCategoryFilter("latestVersion.width", i18n.get("map.width"), mapSizeMap); searchController.addCategoryFilter("latestVersion.height", i18n.get("map.height"), mapSizeMap); - searchController.addRangeFilter("latestVersion.maxPlayers", i18n.get("map.maxPlayers"), 0, 16, 1, Double::intValue); + searchController.addRangeFilter("latestVersion.maxPlayers", i18n.get("map.maxPlayers"), 0, 16, 1, 0); + searchController.addRangeFilter("reviewsSummary.averageScore", i18n.get("reviews.averageScore"), 0, 5, 0.5, 1); searchController.addToggleFilter("latestVersion.ranked", i18n.get("map.onlyRanked"), "true"); } diff --git a/src/main/java/com/faforever/client/mod/ModVaultController.java b/src/main/java/com/faforever/client/mod/ModVaultController.java index 073f3132fc..6802e2a890 100644 --- a/src/main/java/com/faforever/client/mod/ModVaultController.java +++ b/src/main/java/com/faforever/client/mod/ModVaultController.java @@ -152,6 +152,8 @@ protected void initSearchController() { searchController.addTextFilter("author", i18n.get("mod.author"), false); searchController.addDateRangeFilter("latestVersion.updateTime", i18n.get("mod.uploadedDateTime"), 0); + searchController.addRangeFilter("reviewsSummary.averageScore", i18n.get("reviews.averageScore"), 0, 5, 0.5, 1); + searchController.addBinaryFilter("latestVersion.type", i18n.get("mod.type"), ModType.UI.toString(), ModType.SIM.toString(), i18n.get("modType.ui"), i18n.get("modType.sim")); searchController.addToggleFilter("latestVersion.ranked", i18n.get("mod.onlyRanked"), "true"); diff --git a/src/main/java/com/faforever/client/query/RangeFilterController.java b/src/main/java/com/faforever/client/query/RangeFilterController.java index 94915c53ce..d7aacb1e5f 100644 --- a/src/main/java/com/faforever/client/query/RangeFilterController.java +++ b/src/main/java/com/faforever/client/query/RangeFilterController.java @@ -11,11 +11,16 @@ import javafx.scene.control.MenuButton; import javafx.scene.control.TextField; import lombok.Data; +import lombok.SneakyThrows; import org.controlsfx.control.RangeSlider; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParseException; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -35,26 +40,31 @@ public class RangeFilterController implements FilterNodeController { private String propertyName; private Function valueTransform; + private int numberOfFractionDigits; + private DecimalFormat numberFormat; public void initialize() { JavaFxUtil.bindManagedToVisible(menu); rangeSlider.setShowTickMarks(true); rangeSlider.setShowTickLabels(true); rangeSlider.setMinorTickCount(0); - valueTransform = (value) -> value; + valueTransform = Function.identity(); } + @SneakyThrows public Optional> getCondition() { List conditions = new ArrayList<>(); - if (!lowValue.getText().isBlank() && rangeSlider.getLowValue() > rangeSlider.getMin()) { + double lowValueValue = numberFormat.parse(numberFormat.format(rangeSlider.getLowValue())).doubleValue(); + if (!lowValue.getText().isBlank() && lowValueValue > rangeSlider.getMin()) { QBuilder qBuilderLow = new QBuilder<>(); DoubleProperty propertyLow = qBuilderLow.doubleNum(propertyName); - conditions.add(propertyLow.gte(valueTransform.apply(rangeSlider.getLowValue()))); + conditions.add(propertyLow.gte(valueTransform.apply(lowValueValue))); } - if (!highValue.getText().isBlank() && rangeSlider.getHighValue() < rangeSlider.getMax()) { + double highValueValue = numberFormat.parse(numberFormat.format(rangeSlider.getHighValue())).doubleValue(); + if (!highValue.getText().isBlank() && highValueValue < rangeSlider.getMax()) { QBuilder qBuilderHigh = new QBuilder<>(); DoubleProperty propertyHigh = qBuilderHigh.doubleNum(propertyName); - conditions.add(propertyHigh.lte(valueTransform.apply(rangeSlider.getHighValue()))); + conditions.add(propertyHigh.lte(valueTransform.apply(highValueValue))); } if (!conditions.isEmpty()) { if (!menu.getStyleClass().contains("query-filter-selected")) { @@ -92,8 +102,6 @@ public void setMinMax(double min, double max) { rangeSlider.setMax(max); rangeSlider.setHighValue(max); highValue.setText(""); - - JavaFxUtil.bindTextFieldAndRangeSlider(lowValue, highValue, rangeSlider); } public void setIncrement(double increment) { @@ -112,6 +120,18 @@ public void setValueTransform(Function valueTransform) this.valueTransform = valueTransform; } + public void setNumberOfFractionDigits(int numberOfFractionDigits) { + this.numberOfFractionDigits = numberOfFractionDigits; + } + + public void bind() { + numberFormat = (DecimalFormat) DecimalFormat.getInstance(); + numberFormat.setRoundingMode(RoundingMode.HALF_UP); + numberFormat.setMinimumFractionDigits(0); + numberFormat.setMaximumFractionDigits(numberOfFractionDigits); + JavaFxUtil.bindTextFieldAndRangeSlider(lowValue, highValue, rangeSlider, numberFormat); + } + @Override public Node getRoot() { return menu; diff --git a/src/main/java/com/faforever/client/query/SearchablePropertyMappings.java b/src/main/java/com/faforever/client/query/SearchablePropertyMappings.java index 29f06f13d6..ad211aff98 100644 --- a/src/main/java/com/faforever/client/query/SearchablePropertyMappings.java +++ b/src/main/java/com/faforever/client/query/SearchablePropertyMappings.java @@ -37,6 +37,7 @@ public class SearchablePropertyMappings { .put("mapVersion.height", new Property("map.heightPixels", false)) .put("mapVersion.folderName", new Property("game.map.folderName", false)) .put("mapVersion.map.author.login", new Property("game.map.author", false)) + .put("reviewsSummary.averageScore", new Property("reviews.averageScore", true)) .build(); diff --git a/src/main/java/com/faforever/client/replay/OnlineReplayVaultController.java b/src/main/java/com/faforever/client/replay/OnlineReplayVaultController.java index 86ef535379..a23018294c 100644 --- a/src/main/java/com/faforever/client/replay/OnlineReplayVaultController.java +++ b/src/main/java/com/faforever/client/replay/OnlineReplayVaultController.java @@ -150,10 +150,12 @@ protected void initSearchController() { //TODO: Use rating rather than estimated mean with an assumed deviation of 300 when that is available searchController.addRangeFilter("playerStats.ratingChanges.meanBefore", i18n.get("game.rating"), - MIN_RATING, MAX_RATING, 100, (value) -> value + 300); + MIN_RATING, MAX_RATING, 100, 0, value -> value + 300); + + searchController.addRangeFilter("reviewsSummary.averageScore", i18n.get("reviews.averageScore"),0, 5, 0.5, 1); searchController.addDateRangeFilter("endTime", i18n.get("game.date"), 1); - searchController.addRangeFilter("replayTicks", i18n.get("game.duration"), 0, 60, 1, value -> (int) (value*60*10)); + searchController.addRangeFilter("replayTicks", i18n.get("game.duration"), 0, 60, 6, 0, value -> (int) (value*60*10)); searchController.addToggleFilter("validity", i18n.get("game.onlyRanked"), "VALID"); } diff --git a/src/main/java/com/faforever/client/vault/search/SearchController.java b/src/main/java/com/faforever/client/vault/search/SearchController.java index ae0d212c5c..6d4ecdbd9e 100644 --- a/src/main/java/com/faforever/client/vault/search/SearchController.java +++ b/src/main/java/com/faforever/client/vault/search/SearchController.java @@ -39,6 +39,7 @@ import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import java.text.NumberFormat; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; @@ -303,8 +304,14 @@ public CategoryFilterController addCategoryFilter(String propertyName, String ti return categoryFilterController; } - public RangeFilterController addRangeFilter(String propertyName, String title, double min, double max, - double tickUnit, Function valueTransform) { + public void addRangeFilter(String propertyName, String title, double min, double max, + double tickUnit, int numberOfFractionDigits) { + addRangeFilter(propertyName, title, min, max, tickUnit, numberOfFractionDigits, Function.identity()); + } + + public void addRangeFilter(String propertyName, String title, double min, double max, + double tickUnit, int numberOfFractionDigits, + Function valueTransform) { RangeFilterController rangeFilterController = uiService.loadFxml("theme/vault/search/rangeFilter.fxml"); rangeFilterController.setTitle(title); rangeFilterController.setPropertyName(propertyName); @@ -312,9 +319,10 @@ public RangeFilterController addRangeFilter(String propertyName, String title, d rangeFilterController.setIncrement(tickUnit); rangeFilterController.setTickUnit(tickUnit); rangeFilterController.setSnapToTicks(true); + rangeFilterController.setNumberOfFractionDigits(numberOfFractionDigits); rangeFilterController.setValueTransform(valueTransform); + rangeFilterController.bind(); addFilterNode(rangeFilterController); - return rangeFilterController; } public DateRangeFilterController addDateRangeFilter(String propertyName, String title, int initialYearsBefore) { diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index 1504e75536..002774d8dd 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -1269,6 +1269,7 @@ game.create.generatedMap2 = Generate the map settings.fa.allowIpv6 = Allow the ICE adapter to use IPv6 settings.fa.allowIpv6.description = Ipv6 causes connection issues for some players. Turn this on if you do not have any IPv6 issues with connections home.directory.warning.cyrillic = Warning\: Cyrillic characters in the home directory path may cause problems. Please, avoid using them. +reviews.averageScore = Average reviews score ignoreWarning = Ignore warning replay.replayRunning = Replay could not be started because another replay is already running. teammatchmaking.queue.tmm3v3 = 3v3 diff --git a/src/test/java/com/faforever/client/query/RangeFilterControllerTest.java b/src/test/java/com/faforever/client/query/RangeFilterControllerTest.java index 4a470169ec..d96dc8f10f 100644 --- a/src/test/java/com/faforever/client/query/RangeFilterControllerTest.java +++ b/src/test/java/com/faforever/client/query/RangeFilterControllerTest.java @@ -49,6 +49,7 @@ public void setUp() throws Exception { instance.setSnapToTicks(true); instance.setTickUnit(increment); instance.setValueTransform((value) -> value); + instance.bind(); } @Test @@ -88,7 +89,7 @@ public void testAddListener() throws Exception { instance.rangeSlider.setHighValue(90.0); instance.lowValue.setText("20"); instance.highValue.setText("80"); - verify(queryListener, times(12)).invalidated(any()); + verify(queryListener, times(20)).invalidated(any()); } @Test