From 6deeabf6b02b34b3e7620d95e88c2d67076c70df Mon Sep 17 00:00:00 2001 From: Gabriel Diaz Date: Mon, 23 Oct 2023 09:28:45 -0500 Subject: [PATCH] Added factory for changing country flags --- .../dlsc/gemsfx/demo/PhoneNumberFieldApp.java | 11 +-- .../com/dlsc/gemsfx/PhoneNumberField.java | 82 +++++++++++++++---- .../gemsfx/skins/PhoneNumberFieldSkin.java | 41 ++++++++-- 3 files changed, 100 insertions(+), 34 deletions(-) diff --git a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/PhoneNumberFieldApp.java b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/PhoneNumberFieldApp.java index 0f048046..8a9eb5ba 100644 --- a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/PhoneNumberFieldApp.java +++ b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/PhoneNumberFieldApp.java @@ -13,7 +13,6 @@ import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.Separator; -import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; @@ -29,7 +28,7 @@ public class PhoneNumberFieldApp extends Application { return null; } PhoneNumberField.CountryCallingCode code = (PhoneNumberField.CountryCallingCode) c; - return "(+" + code.countryCode() + ") " + code.displayName("en"); + return "(+" + code.countryCode() + ") " + code; }; @Override @@ -42,12 +41,12 @@ public void start(Stage stage) throws Exception { addControl("Preferred Countries", preferredCountriesSelector(field), controls); addControl("Force Local Phone", forceLocalPhoneNumberCheck(field), controls); addControl("Fixed Country", fixedCountrySelector(field), controls); - addControl("Mask", maskInputField(field), controls); VBox fields = new VBox(10); addField(fields, "Country Code", field.countryCodeProperty(), COUNTRY_CODE_CONVERTER); addField(fields, "Phone Number", field.phoneNumberProperty()); addField(fields, "Local Number", field.localPhoneNumberProperty()); + addField(fields, "Mask", field.maskProperty()); VBox vBox = new VBox(20); vBox.setPadding(new Insets(20)); @@ -106,12 +105,6 @@ private Node preferredCountriesSelector(PhoneNumberField view) { return comboBox; } - private Node maskInputField(PhoneNumberField field) { - TextField mask = new TextField(); - mask.textProperty().bindBidirectional(field.maskProperty()); - return mask; - } - private Node forceLocalPhoneNumberCheck(PhoneNumberField field) { CheckBox localCheck = new CheckBox(); localCheck.selectedProperty().bindBidirectional(field.forceLocalPhoneNumberProperty()); diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/PhoneNumberField.java b/gemsfx/src/main/java/com/dlsc/gemsfx/PhoneNumberField.java index 597e3f16..85614290 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/PhoneNumberField.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/PhoneNumberField.java @@ -18,15 +18,13 @@ import javafx.scene.control.Skin; import javafx.scene.control.TextField; import javafx.scene.control.TextFormatter; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; +import javafx.util.Callback; import javafx.util.Pair; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -36,6 +34,7 @@ public class PhoneNumberField extends Control { public static final String DEFAULT_STYLE_CLASS = "phone-number-field"; + public static final String DEFAULT_MASK = "(###) ###-##-##"; private final TextField textField = new TextField(); @@ -142,6 +141,8 @@ public void set(CountryCallingCode countryCallingCode) { .map(CountryCallingCode::defaultPhonePrefix) .orElse(null)); + Optional.ofNullable(getMaskProvider()).ifPresent(m -> setMask(m.call(countryCallingCode))); + } finally { selfUpdate = false; } @@ -252,18 +253,53 @@ public final void setForceLocalPhoneNumber(boolean forceLocalPhoneNumber) { forceLocalPhoneNumberProperty().set(forceLocalPhoneNumber); } - private final StringProperty mask = new SimpleStringProperty(this, "mask"); + private final ObjectProperty> maskProvider = new SimpleObjectProperty<>(this, "maskProvider", code -> + Optional.ofNullable(code).map(CountryCallingCode::mask).orElse(null)) { + @Override + public void set(Callback newMaskProvider) { + super.set(newMaskProvider); + setMask(Optional.ofNullable(newMaskProvider).map(p -> p.call(getCountryCode())).orElse(null)); + } + }; - public final StringProperty maskProperty() { - return mask; + public final ObjectProperty> maskProviderProperty() { + return maskProvider; + } + + public final Callback getMaskProvider() { + return maskProviderProperty().get(); + } + + public final void setMaskProvider(Callback mask) { + maskProviderProperty().set(mask); + } + + private final ReadOnlyStringWrapper mask = new ReadOnlyStringWrapper(this, "mask"); + + public final ReadOnlyStringProperty maskProperty() { + return mask.getReadOnlyProperty(); } public final String getMask() { - return maskProperty().get(); + return mask.get(); } - public final void setMask(String mask) { - maskProperty().set(mask); + private void setMask(String mask) { + this.mask.set(mask); + } + + private final ObjectProperty> countryCodeViewFactory = new SimpleObjectProperty<>(this, "countryCodeViewFactory"); + + public final ObjectProperty> countryCodeViewFactoryProperty() { + return countryCodeViewFactory; + } + + public final Callback getCountryCodeViewFactory() { + return countryCodeViewFactoryProperty().get(); + } + + public final void setCountryCodeViewFactory(Callback countryCodeViewFactory) { + countryCodeViewFactoryProperty().set(countryCodeViewFactory); } public interface CountryCallingCode { @@ -272,9 +308,9 @@ public interface CountryCallingCode { int[] areaCodes(); - String displayName(String language); + String iso2Code(); - Node flagView(); + String mask(); default Integer defaultAreaCode() { return areaCodes().length > 0 ? areaCodes()[0] : null; @@ -540,13 +576,17 @@ enum Defaults implements CountryCallingCode { private final int code; private final String iso2Code; private final int[] areaCodes; - private final Image flagImage; + private final String mask; Defaults(int code, String iso2Code, int... areaCodes) { + this(code, iso2Code, DEFAULT_MASK, areaCodes); + } + + Defaults(int code, String iso2Code, String mask, int... areaCodes) { this.code = code; this.iso2Code = iso2Code; + this.mask = mask; this.areaCodes = Optional.ofNullable(areaCodes).orElse(new int[0]); - this.flagImage = new Image(Objects.requireNonNull(PhoneNumberField.class.getResource("phonenumberfield/country-flags/20x13/" + iso2Code.toLowerCase() + ".png")).toExternalForm()); } @Override @@ -560,13 +600,13 @@ public int[] areaCodes() { } @Override - public String displayName(String language) { - return new Locale(language, iso2Code).getDisplayCountry(); + public String iso2Code() { + return iso2Code; } @Override - public Node flagView() { - return new ImageView(flagImage); + public String mask() { + return mask; } } @@ -642,6 +682,13 @@ private static Pair scoreAndLocalNumber(CountryCallingCode code } private final class PhoneNumberFormatter implements UnaryOperator { + + private PhoneNumberFormatter() { + maskProperty().addListener((observable, oldMask, newMask) -> { + + }); + } + @Override public TextFormatter.Change apply(TextFormatter.Change change) { if (change.isAdded() || change.isReplaced()) { @@ -660,6 +707,7 @@ public TextFormatter.Change apply(TextFormatter.Change change) { } } } + return change; } } diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/skins/PhoneNumberFieldSkin.java b/gemsfx/src/main/java/com/dlsc/gemsfx/skins/PhoneNumberFieldSkin.java index a9eaa0dc..af945980 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/skins/PhoneNumberFieldSkin.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/skins/PhoneNumberFieldSkin.java @@ -20,7 +20,10 @@ import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; +import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -29,6 +32,14 @@ public class PhoneNumberFieldSkin extends SkinBase { private static final Image WORLD_ICON = new Image(Objects.requireNonNull(PhoneNumberField.class.getResource("phonenumberfield/world.png")).toExternalForm()); + private static final Map FLAG_IMAGES = new HashMap<>(); + + static { + for (PhoneNumberField.CountryCallingCode code : PhoneNumberField.CountryCallingCode.Defaults.values()) { + FLAG_IMAGES.put(code, new Image(Objects.requireNonNull(PhoneNumberField.class.getResource("phonenumberfield/country-flags/20x13/" + code.iso2Code().toLowerCase() + ".png")).toExternalForm())); + } + } + public PhoneNumberFieldSkin(PhoneNumberField field, ReadOnlyObjectWrapper countryCode) { super(field); @@ -49,8 +60,10 @@ public PhoneNumberFieldSkin(PhoneNumberField field, ReadOnlyObjectWrapper callingCodesUpdater.run()); - getSkinnable().getPreferredCountryCodes().addListener((InvalidationListener) obs -> callingCodesUpdater.run()); + InvalidationListener listener = obs -> callingCodesUpdater.run(); + getSkinnable().getAvailableCountryCodes().addListener(listener); + getSkinnable().getPreferredCountryCodes().addListener(listener); + getSkinnable().countryCodeViewFactoryProperty().addListener(listener); callingCodesUpdater.run(); PhoneNumberEditor editor = new PhoneNumberEditor(); @@ -105,11 +118,9 @@ public PhoneNumberEditor() { StackPane flagBox = new StackPane(); flagBox.getStyleClass().add("flag-box"); - Runnable flagUpdater = () -> flagBox.getChildren().setAll(Optional.ofNullable(getSkinnable().getCountryCode()) - .map(PhoneNumberField.CountryCallingCode::flagView) - .orElse(new ImageView(WORLD_ICON))); - + Runnable flagUpdater = () -> flagBox.getChildren().setAll(getCountryCodeFlagView(getSkinnable().getCountryCode())); getSkinnable().countryCodeProperty().addListener(obs -> flagUpdater.run()); + getSkinnable().countryCodeViewFactoryProperty().addListener(obs -> flagUpdater.run()); flagUpdater.run(); Region arrow = new Region(); @@ -177,8 +188,8 @@ protected void updateItem(PhoneNumberField.CountryCallingCode item, boolean empt int index = -1; if (item != null && !empty) { - setText("(+" + item.countryCode() + ") " + item.displayName("en")); - setGraphic(item.flagView()); + setText("(+" + item.countryCode() + ") " + new Locale("en", item.iso2Code()).getDisplayCountry()); + setGraphic(getCountryCodeFlagView(item)); index = getSkinnable().getPreferredCountryCodes().indexOf(item); } else { setText(null); @@ -200,4 +211,18 @@ protected void updateItem(PhoneNumberField.CountryCallingCode item, boolean empt } + private Node getCountryCodeFlagView(PhoneNumberField.CountryCallingCode code) { + Node flagView; + if (code != null) { + if (getSkinnable().getCountryCodeViewFactory() != null) { + flagView = getSkinnable().getCountryCodeViewFactory().call(code); + } else { + flagView = new ImageView(Optional.ofNullable(FLAG_IMAGES.get(code)).orElse(WORLD_ICON)); + } + } else { + flagView = new ImageView(WORLD_ICON); + } + return flagView; + } + }