PreferencesFX icon indicating copy to clipboard operation
PreferencesFX copied to clipboard

Locale setting type

Open trixon opened this issue 4 years ago • 5 comments

Inspired by the Color setting I started working on a java.util.Locale setting. It kind of works but I have two problems.

  1. The setting stored is the setting displayed (in the SimpleComboBoxControl) In order for this setting to survive a language change and be human readable I must store the locale.toLanguageTag() and display locale.getDisplayLanguage().

  2. Having about 160 locales, SimpleComboBoxControl.comboBox.setVisibleRowCount(4) is somewhat limited.

Any pointers on how to improve this?

If it matters, I use the locale setting on String.format and SimpleDateFormat, not the UI itself.

trixon avatar Aug 23 '19 06:08 trixon

First off, cool, I'm pretty sure that would make a great addition to PreferencesFX!

  1. If I understood your issue correctly, you could use the Locale objects as items directly for the Combobox and then by calling something like setCellValueFactory( locale -> locale.getDisplayLanguage() ) on the combo box control directly you can customize it to show the locale in the way you want it to be. You can then store the Locale object itself, from which you can get toLanguageTag() if you need to. If there was a misunderstanding please tell me!

  2. You could just override this with a different row count in your control.

I assume you already did it in this way, but in your case I would create a new class, extending the SimpleComboboxControl and then you can apply the changes you need on top of that for your Locale control.

martinfrancois avatar Aug 23 '19 11:08 martinfrancois

I avoided extending SimpleComboboxControl, but I'll do just that.

trixon avatar Aug 24 '19 11:08 trixon

@trixon did it work?

martinfrancois avatar Sep 01 '19 14:09 martinfrancois

@martinfrancois I have not got around to it yet, it's on the list though.

trixon avatar Sep 02 '19 17:09 trixon

@martinfrancois Ok, this is what I have so far, and it looks likes it's working.

    /**
     * Creates a custom locale control.
     *
     * @param description the title of this setting
     * @param localeProperty the current selected locale value
     * @param locales the selectable locales, all if null or empty
     * @return the constructed setting
     */
    public static Setting of(String description, ObjectProperty<Locale> localeProperty, ObservableList<Locale> locales) {
        StringProperty stringProperty = new SimpleStringProperty();
        stringProperty.bindBidirectional(localeProperty, new StringConverter<Locale>() {
            @Override
            public String toString(Locale locale) {
                return Objects.isNull(locale) ? Locale.getDefault().toLanguageTag() : locale.toLanguageTag();
            }

            @Override
            public Locale fromString(String value) {
                return Objects.isNull(value) ? Locale.getDefault() : Locale.forLanguageTag(value);
            }
        });

        if (Objects.isNull(locales) || locales.isEmpty()) {
            locales = Arrays.stream(Locale.getAvailableLocales())
                    .sorted((Locale o1, Locale o2) -> o1.getDisplayName().compareTo(o2.getDisplayName()))
                    .collect(Collectors.collectingAndThen(Collectors.toList(), l -> FXCollections.observableArrayList(l)));
            locales.remove(0);
        }

        SimpleListProperty<Locale> localesProperty = new SimpleListProperty<>(locales);

        return new Setting<>(
                description,
                Field.ofSingleSelectionType(localesProperty, localeProperty)
                        .label(description)
                        .render(new SimpleLocaleComboBoxControl()),
                stringProperty);
    }

public class SimpleLocaleComboBoxControl extends SimpleComboBoxControl {

    public SimpleLocaleComboBoxControl() {

    }

    @Override
    public void layoutParts() {
        super.layoutParts();

        try {
            java.lang.reflect.Field comboBoxField = getClass().getSuperclass().getDeclaredField("comboBox");
            comboBoxField.setAccessible(true);
            ComboBox<Locale> comboBox = (ComboBox) comboBoxField.get(this);
            comboBox.setVisibleRowCount(16);
            comboBox.setConverter(new StringConverter<Locale>() {
                @Override
                public Locale fromString(String string) {
                    return null;
                }

                @Override
                public String toString(Locale locale) {
                    return locale.getDisplayName();
                }
            });
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
            Logger.getLogger(SimpleLocaleComboBoxControl.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

A couple of things...

  1. I'm not that happy about the reflection part, it was just a way forward. Could we add a getter for the combobox or make it default/protected?
  2. I had to came up with a unique method signature since the color setting already "claims" of(String description, ObjectProperty<> property), which after all wasn't that bad, limiting the locales can be useful. But for the future there might be good to have convention supporting multiple ObjectProperty settings.
  3. I have no idea how to get my code, if accepted, into the repository. For one thing, I have already messed up the code formatting...
  4. I have not used this feature much yet.

trixon avatar Sep 17 '19 15:09 trixon