Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git

Work around PreferencesFX/JavaFX bug

AuthorDaveJarvis <email>
Date2021-12-21 20:01:38 GMT-0800
Commitb9f0a1386c6c5da89e498a93dd4de3613e25e864
Parenta67e8ba
Delta402 lines added, 376 lines removed, 26-line increase
src/main/java/com/keenwrite/ui/common/CellEditor.java
public class CellEditor {
private FocusListener mFocusListener;
+ private final KeyHandler mKeyHandler = new KeyHandler();
private final Property<String> mInputText = new SimpleStringProperty();
private final Consumer<String> mConsumer;
private void init( final ObjectProperty<Node> graphicProperty ) {
- final var keyHandler = new KeyHandler();
-
// When the text field is added as the graphics context, we hook into
// the changed value to get a handle on the text field. From there it is
// possible to add change the keyboard and focus behaviours.
graphicProperty.addListener( ( c, o, n ) -> {
if( o instanceof TextField ) {
- o.removeEventHandler( KEY_RELEASED, keyHandler );
+ o.removeEventHandler( KEY_RELEASED, mKeyHandler );
o.focusedProperty().removeListener( mFocusListener );
}
if( n instanceof final TextField input ) {
- n.addEventFilter( KEY_RELEASED, keyHandler );
+ n.addEventFilter( KEY_RELEASED, mKeyHandler );
mInputText.bind( input.textProperty() );
mFocusListener = new FocusListener( input );
src/main/java/com/keenwrite/preferences/MapProperty.java
+/* Copyright 2021 White Magic Software, Ltd. -- All rights reserved. */
package com.keenwrite.preferences;
src/main/java/com/keenwrite/preferences/PreferencesController.java
import java.io.File;
-import java.util.Map;
-
-import static com.dlsc.formsfx.model.structure.Field.ofStringType;
-import static com.dlsc.preferencesfx.PreferencesFxEvent.EVENT_PREFERENCES_SAVED;
-import static com.keenwrite.Messages.get;
-import static com.keenwrite.constants.GraphicsConstants.ICON_DIALOG;
-import static com.keenwrite.preferences.AppKeys.*;
-import static com.keenwrite.preferences.LocaleProperty.localeListProperty;
-import static com.keenwrite.preferences.MapField.ofMapType;
-import static com.keenwrite.preferences.SkinProperty.skinListProperty;
-import static javafx.scene.control.ButtonType.CANCEL;
-import static javafx.scene.control.ButtonType.OK;
-
-/**
- * Provides the ability for users to configure their preferences. This links
- * the {@link Workspace} model with the {@link PreferencesFx} view, in MVC.
- */
-@SuppressWarnings( "SameParameterValue" )
-public final class PreferencesController {
-
- private final Workspace mWorkspace;
- private final PreferencesFx mPreferencesFx;
-
- public PreferencesController( final Workspace workspace ) {
- mWorkspace = workspace;
-
- // All properties must be initialized before creating the dialog.
- mPreferencesFx = createPreferencesFx();
-
- initKeyEventHandler( mPreferencesFx );
- }
-
- /**
- * Display the user preferences settings dialog (non-modal).
- */
- public void show() {
- getPreferencesFx().show( false );
- }
-
- /**
- * Call to persist the settings. Strictly speaking, this could watch on
- * all values for external changes then save automatically.
- */
- public void save() {
- getPreferencesFx().saveSettings();
- }
-
- /**
- * Delegates to the {@link PreferencesFx} event handler for monitoring
- * save events.
- *
- * @param eventHandler The handler to call when the preferences are saved.
- */
- public void addSaveEventHandler(
- final EventHandler<? super PreferencesFxEvent> eventHandler ) {
- getPreferencesFx().addEventHandler( EVENT_PREFERENCES_SAVED, eventHandler );
- }
-
- private StringField createFontNameField(
- final StringProperty fontName, final DoubleProperty fontSize ) {
- final var control = new SimpleFontControl( "Change" );
- control.fontSizeProperty().addListener( ( c, o, n ) -> {
- if( n != null ) {
- fontSize.set( n.doubleValue() );
- }
- } );
- return ofStringType( fontName ).render( control );
- }
-
- private <K, V> MapField<K, V> createMapField(
- final ObjectProperty<Map<K, V>> map ) {
- final var control = new SimpleTableControl<>( map.get() );
-
- return ofMapType( map ).render( control );
- }
-
- /**
- * Creates the preferences dialog based using {@link XmlStorageHandler} and
- * numerous {@link Category} objects.
- *
- * @return A component for editing preferences.
- * @throws RuntimeException Could not construct the {@link PreferencesFx}
- * object (e.g., illegal access permissions,
- * unmapped XML resource).
- */
- private PreferencesFx createPreferencesFx() {
- return PreferencesFx.of( createStorageHandler(), createCategories() )
- .instantPersistent( false )
- .dialogIcon( ICON_DIALOG );
- }
-
- private StorageHandler createStorageHandler() {
- return new XmlStorageHandler();
- }
-
- private Category[] createCategories() {
- return new Category[]{
- Category.of(
- get( KEY_DOC ),
- Group.of(
- get( KEY_DOC_META ),
- Setting.of( label( KEY_DOC_META ) ),
- Setting.of( title( KEY_DOC_META ),
- createMapField( mapProperty( KEY_DOC_META ) ),
- mapProperty( KEY_DOC_META ) )
- ),
- Group.of(
- get( KEY_DOC_TITLE ),
- Setting.of( label( KEY_DOC_TITLE ) ),
- Setting.of( title( KEY_DOC_TITLE ),
- stringProperty( KEY_DOC_TITLE ) )
- ),
- Group.of(
- get( KEY_DOC_AUTHOR ),
- Setting.of( label( KEY_DOC_AUTHOR ) ),
- Setting.of( title( KEY_DOC_AUTHOR ),
- stringProperty( KEY_DOC_AUTHOR ) )
- ),
- Group.of(
- get( KEY_DOC_BYLINE ),
- Setting.of( label( KEY_DOC_BYLINE ) ),
- Setting.of( title( KEY_DOC_BYLINE ),
- stringProperty( KEY_DOC_BYLINE ) )
- ),
- Group.of(
- get( KEY_DOC_ADDRESS ),
- Setting.of( label( KEY_DOC_ADDRESS ) ),
- createMultilineSetting( "Address", KEY_DOC_ADDRESS )
- ),
- Group.of(
- get( KEY_DOC_PHONE ),
- Setting.of( label( KEY_DOC_PHONE ) ),
- Setting.of( title( KEY_DOC_PHONE ),
- stringProperty( KEY_DOC_PHONE ) )
- ),
- Group.of(
- get( KEY_DOC_EMAIL ),
- Setting.of( label( KEY_DOC_EMAIL ) ),
- Setting.of( title( KEY_DOC_EMAIL ),
- stringProperty( KEY_DOC_EMAIL ) )
- ),
- Group.of(
- get( KEY_DOC_KEYWORDS ),
- Setting.of( label( KEY_DOC_KEYWORDS ) ),
- Setting.of( title( KEY_DOC_KEYWORDS ),
- stringProperty( KEY_DOC_KEYWORDS ) )
- ),
- Group.of(
- get( KEY_DOC_COPYRIGHT ),
- Setting.of( label( KEY_DOC_COPYRIGHT ) ),
- Setting.of( title( KEY_DOC_COPYRIGHT ),
- stringProperty( KEY_DOC_COPYRIGHT ) )
- ),
- Group.of(
- get( KEY_DOC_DATE ),
- Setting.of( label( KEY_DOC_DATE ) ),
- Setting.of( title( KEY_DOC_DATE ),
- stringProperty( KEY_DOC_DATE ) )
- )
- ),
- Category.of(
- get( KEY_TYPESET ),
- Group.of(
- get( KEY_TYPESET_CONTEXT ),
- Setting.of( label( KEY_TYPESET_CONTEXT_THEMES_PATH ) ),
- Setting.of( title( KEY_TYPESET_CONTEXT_THEMES_PATH ),
- fileProperty( KEY_TYPESET_CONTEXT_THEMES_PATH ), true ),
- Setting.of( label( KEY_TYPESET_CONTEXT_CLEAN ) ),
- Setting.of( title( KEY_TYPESET_CONTEXT_CLEAN ),
- booleanProperty( KEY_TYPESET_CONTEXT_CLEAN ) )
- ),
- Group.of(
- get( KEY_TYPESET_TYPOGRAPHY ),
- Setting.of( label( KEY_TYPESET_TYPOGRAPHY_QUOTES ) ),
- Setting.of( title( KEY_TYPESET_TYPOGRAPHY_QUOTES ),
- booleanProperty( KEY_TYPESET_TYPOGRAPHY_QUOTES ) )
- )
- ),
- Category.of(
- get( KEY_EDITOR ),
- Group.of(
- get( KEY_EDITOR_AUTOSAVE ),
- Setting.of( label( KEY_EDITOR_AUTOSAVE ) ),
- Setting.of( title( KEY_EDITOR_AUTOSAVE ),
- integerProperty( KEY_EDITOR_AUTOSAVE ) )
- )
- ),
- Category.of(
- get( KEY_R ),
- Group.of(
- get( KEY_R_DIR ),
- Setting.of( label( KEY_R_DIR,
- stringProperty( KEY_DEF_DELIM_BEGAN ).get(),
- stringProperty( KEY_DEF_DELIM_ENDED ).get() ) ),
- Setting.of( title( KEY_R_DIR ),
- fileProperty( KEY_R_DIR ), true )
- ),
- Group.of(
- get( KEY_R_SCRIPT ),
- Setting.of( label( KEY_R_SCRIPT ) ),
- createMultilineSetting( "Script", KEY_R_SCRIPT )
- ),
- Group.of(
- get( KEY_R_DELIM_BEGAN ),
- Setting.of( label( KEY_R_DELIM_BEGAN ) ),
- Setting.of( title( KEY_R_DELIM_BEGAN ),
- stringProperty( KEY_R_DELIM_BEGAN ) )
- ),
- Group.of(
- get( KEY_R_DELIM_ENDED ),
- Setting.of( label( KEY_R_DELIM_ENDED ) ),
- Setting.of( title( KEY_R_DELIM_ENDED ),
- stringProperty( KEY_R_DELIM_ENDED ) )
- )
- ),
- Category.of(
- get( KEY_IMAGES ),
- Group.of(
- get( KEY_IMAGES_DIR ),
- Setting.of( label( KEY_IMAGES_DIR ) ),
- Setting.of( title( KEY_IMAGES_DIR ),
- fileProperty( KEY_IMAGES_DIR ), true )
- ),
- Group.of(
- get( KEY_IMAGES_ORDER ),
- Setting.of( label( KEY_IMAGES_ORDER ) ),
- Setting.of( title( KEY_IMAGES_ORDER ),
- stringProperty( KEY_IMAGES_ORDER ) )
- ),
- Group.of(
- get( KEY_IMAGES_RESIZE ),
- Setting.of( label( KEY_IMAGES_RESIZE ) ),
- Setting.of( title( KEY_IMAGES_RESIZE ),
- booleanProperty( KEY_IMAGES_RESIZE ) )
- ),
- Group.of(
- get( KEY_IMAGES_SERVER ),
- Setting.of( label( KEY_IMAGES_SERVER ) ),
- Setting.of( title( KEY_IMAGES_SERVER ),
- stringProperty( KEY_IMAGES_SERVER ) )
- )
- ),
- Category.of(
- get( KEY_DEF ),
- Group.of(
- get( KEY_DEF_PATH ),
- Setting.of( label( KEY_DEF_PATH ) ),
- Setting.of( title( KEY_DEF_PATH ),
- fileProperty( KEY_DEF_PATH ), false )
- ),
- Group.of(
- get( KEY_DEF_DELIM_BEGAN ),
- Setting.of( label( KEY_DEF_DELIM_BEGAN ) ),
- Setting.of( title( KEY_DEF_DELIM_BEGAN ),
- stringProperty( KEY_DEF_DELIM_BEGAN ) )
- ),
- Group.of(
- get( KEY_DEF_DELIM_ENDED ),
- Setting.of( label( KEY_DEF_DELIM_ENDED ) ),
- Setting.of( title( KEY_DEF_DELIM_ENDED ),
- stringProperty( KEY_DEF_DELIM_ENDED ) )
- )
- ),
- Category.of(
- get( KEY_UI_FONT ),
- Group.of(
- get( KEY_UI_FONT_EDITOR ),
- Setting.of( label( KEY_UI_FONT_EDITOR_NAME ) ),
- Setting.of( title( KEY_UI_FONT_EDITOR_NAME ),
- createFontNameField(
- stringProperty( KEY_UI_FONT_EDITOR_NAME ),
- doubleProperty( KEY_UI_FONT_EDITOR_SIZE ) ),
- stringProperty( KEY_UI_FONT_EDITOR_NAME ) ),
- Setting.of( label( KEY_UI_FONT_EDITOR_SIZE ) ),
- Setting.of( title( KEY_UI_FONT_EDITOR_SIZE ),
- doubleProperty( KEY_UI_FONT_EDITOR_SIZE ) )
- ),
- Group.of(
- get( KEY_UI_FONT_PREVIEW ),
- Setting.of( label( KEY_UI_FONT_PREVIEW_NAME ) ),
- Setting.of( title( KEY_UI_FONT_PREVIEW_NAME ),
- createFontNameField(
- stringProperty( KEY_UI_FONT_PREVIEW_NAME ),
- doubleProperty( KEY_UI_FONT_PREVIEW_SIZE ) ),
- stringProperty( KEY_UI_FONT_PREVIEW_NAME ) ),
- Setting.of( label( KEY_UI_FONT_PREVIEW_SIZE ) ),
- Setting.of( title( KEY_UI_FONT_PREVIEW_SIZE ),
- doubleProperty( KEY_UI_FONT_PREVIEW_SIZE ) ),
- Setting.of( label( KEY_UI_FONT_PREVIEW_MONO_NAME ) ),
- Setting.of( title( KEY_UI_FONT_PREVIEW_MONO_NAME ),
- createFontNameField(
- stringProperty( KEY_UI_FONT_PREVIEW_MONO_NAME ),
- doubleProperty( KEY_UI_FONT_PREVIEW_MONO_SIZE ) ),
- stringProperty( KEY_UI_FONT_PREVIEW_MONO_NAME ) ),
- Setting.of( label( KEY_UI_FONT_PREVIEW_MONO_SIZE ) ),
- Setting.of( title( KEY_UI_FONT_PREVIEW_MONO_SIZE ),
- doubleProperty( KEY_UI_FONT_PREVIEW_MONO_SIZE ) )
- )
- ),
- Category.of(
- get( KEY_UI_SKIN ),
- Group.of(
- get( KEY_UI_SKIN_SELECTION ),
- Setting.of( label( KEY_UI_SKIN_SELECTION ) ),
- Setting.of( title( KEY_UI_SKIN_SELECTION ),
- skinListProperty(),
- skinProperty( KEY_UI_SKIN_SELECTION ) )
- ),
- Group.of(
- get( KEY_UI_SKIN_CUSTOM ),
- Setting.of( label( KEY_UI_SKIN_CUSTOM ) ),
- Setting.of( title( KEY_UI_SKIN_CUSTOM ),
- fileProperty( KEY_UI_SKIN_CUSTOM ), false )
- )
- ),
- Category.of(
- get( KEY_UI_PREVIEW ),
- Group.of(
- get( KEY_UI_PREVIEW_STYLESHEET ),
- Setting.of( label( KEY_UI_PREVIEW_STYLESHEET ) ),
- Setting.of( title( KEY_UI_PREVIEW_STYLESHEET ),
- fileProperty( KEY_UI_PREVIEW_STYLESHEET ), false )
- )
- ),
- Category.of(
- get( KEY_LANGUAGE ),
- Group.of(
- get( KEY_LANGUAGE_LOCALE ),
- Setting.of( label( KEY_LANGUAGE_LOCALE ) ),
- Setting.of( title( KEY_LANGUAGE_LOCALE ),
- localeListProperty(),
- localeProperty( KEY_LANGUAGE_LOCALE ) )
- )
- )};
- }
-
- @SuppressWarnings( "unchecked" )
- private Setting<StringField, StringProperty> createMultilineSetting(
- final String description, final Key property ) {
- final Setting<StringField, StringProperty> setting =
- Setting.of( description, stringProperty( property ) );
- final var field = setting.getElement();
- field.multiline( true );
-
- return setting;
- }
-
- private void initKeyEventHandler( final PreferencesFx preferences ) {
- final var view = preferences.getView();
- final var nodes = view.getChildrenUnmodifiable();
- final var master = (MasterDetailPane) nodes.get( 0 );
- final var detail = (NavigationView) master.getDetailNode();
- final var pane = (DialogPane) view.getParent();
-
- detail.setOnKeyReleased( ( key ) -> {
+
+import static com.dlsc.formsfx.model.structure.Field.ofStringType;
+import static com.dlsc.preferencesfx.PreferencesFxEvent.EVENT_PREFERENCES_SAVED;
+import static com.keenwrite.Messages.get;
+import static com.keenwrite.constants.GraphicsConstants.ICON_DIALOG;
+import static com.keenwrite.preferences.AppKeys.*;
+import static com.keenwrite.preferences.LocaleProperty.localeListProperty;
+import static com.keenwrite.preferences.MapField.ofMapType;
+import static com.keenwrite.preferences.SkinProperty.skinListProperty;
+import static javafx.scene.control.ButtonType.CANCEL;
+import static javafx.scene.control.ButtonType.OK;
+
+/**
+ * Provides the ability for users to configure their preferences. This links
+ * the {@link Workspace} model with the {@link PreferencesFx} view, in MVC.
+ */
+@SuppressWarnings( "SameParameterValue" )
+public final class PreferencesController {
+
+ private final Workspace mWorkspace;
+ private final PreferencesFx mPreferencesFx;
+
+ public PreferencesController( final Workspace workspace ) {
+ mWorkspace = workspace;
+
+ // All properties must be initialized before creating the dialog.
+ mPreferencesFx = createPreferencesFx();
+
+ initKeyEventHandler( mPreferencesFx );
+ }
+
+ /**
+ * Display the user preferences settings dialog (non-modal).
+ */
+ public void show() {
+ getPreferencesFx().show( false );
+ }
+
+ /**
+ * Call to persist the settings. Strictly speaking, this could watch on
+ * all values for external changes then save automatically.
+ */
+ public void save() {
+ getPreferencesFx().saveSettings();
+ }
+
+ /**
+ * Delegates to the {@link PreferencesFx} event handler for monitoring
+ * save events.
+ *
+ * @param eventHandler The handler to call when the preferences are saved.
+ */
+ public void addSaveEventHandler(
+ final EventHandler<? super PreferencesFxEvent> eventHandler ) {
+ getPreferencesFx().addEventHandler( EVENT_PREFERENCES_SAVED, eventHandler );
+ }
+
+ private StringField createFontNameField(
+ final StringProperty fontName, final DoubleProperty fontSize ) {
+ final var control = new SimpleFontControl( "Change" );
+ control.fontSizeProperty().addListener( ( c, o, n ) -> {
+ if( n != null ) {
+ fontSize.set( n.doubleValue() );
+ }
+ } );
+ return ofStringType( fontName ).render( control );
+ }
+
+ private <K, V> MapField<K, V> createMapField( final MapProperty<K, V> map ) {
+ return ofMapType( map ).render( new SimpleTableControl<>() );
+ }
+
+ /**
+ * Creates the preferences dialog based using {@link XmlStorageHandler} and
+ * numerous {@link Category} objects.
+ *
+ * @return A component for editing preferences.
+ * @throws RuntimeException Could not construct the {@link PreferencesFx}
+ * object (e.g., illegal access permissions,
+ * unmapped XML resource).
+ */
+ private PreferencesFx createPreferencesFx() {
+ return PreferencesFx.of( createStorageHandler(), createCategories() )
+ .instantPersistent( false )
+ .dialogIcon( ICON_DIALOG );
+ }
+
+ private StorageHandler createStorageHandler() {
+ return new XmlStorageHandler();
+ }
+
+ private Category[] createCategories() {
+ return new Category[]{
+ Category.of(
+ get( KEY_DOC ),
+ Group.of(
+ get( KEY_DOC_META ),
+ Setting.of( label( KEY_DOC_META ) ),
+ Setting.of( title( KEY_DOC_META ),
+ createMapField( mapProperty( KEY_DOC_META ) ),
+ mapProperty( KEY_DOC_META ) )
+ ),
+ Group.of(
+ get( KEY_DOC_TITLE ),
+ Setting.of( label( KEY_DOC_TITLE ) ),
+ Setting.of( title( KEY_DOC_TITLE ),
+ stringProperty( KEY_DOC_TITLE ) )
+ ),
+ Group.of(
+ get( KEY_DOC_AUTHOR ),
+ Setting.of( label( KEY_DOC_AUTHOR ) ),
+ Setting.of( title( KEY_DOC_AUTHOR ),
+ stringProperty( KEY_DOC_AUTHOR ) )
+ ),
+ Group.of(
+ get( KEY_DOC_BYLINE ),
+ Setting.of( label( KEY_DOC_BYLINE ) ),
+ Setting.of( title( KEY_DOC_BYLINE ),
+ stringProperty( KEY_DOC_BYLINE ) )
+ ),
+ Group.of(
+ get( KEY_DOC_ADDRESS ),
+ Setting.of( label( KEY_DOC_ADDRESS ) ),
+ createMultilineSetting( "Address", KEY_DOC_ADDRESS )
+ ),
+ Group.of(
+ get( KEY_DOC_PHONE ),
+ Setting.of( label( KEY_DOC_PHONE ) ),
+ Setting.of( title( KEY_DOC_PHONE ),
+ stringProperty( KEY_DOC_PHONE ) )
+ ),
+ Group.of(
+ get( KEY_DOC_EMAIL ),
+ Setting.of( label( KEY_DOC_EMAIL ) ),
+ Setting.of( title( KEY_DOC_EMAIL ),
+ stringProperty( KEY_DOC_EMAIL ) )
+ ),
+ Group.of(
+ get( KEY_DOC_KEYWORDS ),
+ Setting.of( label( KEY_DOC_KEYWORDS ) ),
+ Setting.of( title( KEY_DOC_KEYWORDS ),
+ stringProperty( KEY_DOC_KEYWORDS ) )
+ ),
+ Group.of(
+ get( KEY_DOC_COPYRIGHT ),
+ Setting.of( label( KEY_DOC_COPYRIGHT ) ),
+ Setting.of( title( KEY_DOC_COPYRIGHT ),
+ stringProperty( KEY_DOC_COPYRIGHT ) )
+ ),
+ Group.of(
+ get( KEY_DOC_DATE ),
+ Setting.of( label( KEY_DOC_DATE ) ),
+ Setting.of( title( KEY_DOC_DATE ),
+ stringProperty( KEY_DOC_DATE ) )
+ )
+ ),
+ Category.of(
+ get( KEY_TYPESET ),
+ Group.of(
+ get( KEY_TYPESET_CONTEXT ),
+ Setting.of( label( KEY_TYPESET_CONTEXT_THEMES_PATH ) ),
+ Setting.of( title( KEY_TYPESET_CONTEXT_THEMES_PATH ),
+ fileProperty( KEY_TYPESET_CONTEXT_THEMES_PATH ), true ),
+ Setting.of( label( KEY_TYPESET_CONTEXT_CLEAN ) ),
+ Setting.of( title( KEY_TYPESET_CONTEXT_CLEAN ),
+ booleanProperty( KEY_TYPESET_CONTEXT_CLEAN ) )
+ ),
+ Group.of(
+ get( KEY_TYPESET_TYPOGRAPHY ),
+ Setting.of( label( KEY_TYPESET_TYPOGRAPHY_QUOTES ) ),
+ Setting.of( title( KEY_TYPESET_TYPOGRAPHY_QUOTES ),
+ booleanProperty( KEY_TYPESET_TYPOGRAPHY_QUOTES ) )
+ )
+ ),
+ Category.of(
+ get( KEY_EDITOR ),
+ Group.of(
+ get( KEY_EDITOR_AUTOSAVE ),
+ Setting.of( label( KEY_EDITOR_AUTOSAVE ) ),
+ Setting.of( title( KEY_EDITOR_AUTOSAVE ),
+ integerProperty( KEY_EDITOR_AUTOSAVE ) )
+ )
+ ),
+ Category.of(
+ get( KEY_R ),
+ Group.of(
+ get( KEY_R_DIR ),
+ Setting.of( label( KEY_R_DIR,
+ stringProperty( KEY_DEF_DELIM_BEGAN ).get(),
+ stringProperty( KEY_DEF_DELIM_ENDED ).get() ) ),
+ Setting.of( title( KEY_R_DIR ),
+ fileProperty( KEY_R_DIR ), true )
+ ),
+ Group.of(
+ get( KEY_R_SCRIPT ),
+ Setting.of( label( KEY_R_SCRIPT ) ),
+ createMultilineSetting( "Script", KEY_R_SCRIPT )
+ ),
+ Group.of(
+ get( KEY_R_DELIM_BEGAN ),
+ Setting.of( label( KEY_R_DELIM_BEGAN ) ),
+ Setting.of( title( KEY_R_DELIM_BEGAN ),
+ stringProperty( KEY_R_DELIM_BEGAN ) )
+ ),
+ Group.of(
+ get( KEY_R_DELIM_ENDED ),
+ Setting.of( label( KEY_R_DELIM_ENDED ) ),
+ Setting.of( title( KEY_R_DELIM_ENDED ),
+ stringProperty( KEY_R_DELIM_ENDED ) )
+ )
+ ),
+ Category.of(
+ get( KEY_IMAGES ),
+ Group.of(
+ get( KEY_IMAGES_DIR ),
+ Setting.of( label( KEY_IMAGES_DIR ) ),
+ Setting.of( title( KEY_IMAGES_DIR ),
+ fileProperty( KEY_IMAGES_DIR ), true )
+ ),
+ Group.of(
+ get( KEY_IMAGES_ORDER ),
+ Setting.of( label( KEY_IMAGES_ORDER ) ),
+ Setting.of( title( KEY_IMAGES_ORDER ),
+ stringProperty( KEY_IMAGES_ORDER ) )
+ ),
+ Group.of(
+ get( KEY_IMAGES_RESIZE ),
+ Setting.of( label( KEY_IMAGES_RESIZE ) ),
+ Setting.of( title( KEY_IMAGES_RESIZE ),
+ booleanProperty( KEY_IMAGES_RESIZE ) )
+ ),
+ Group.of(
+ get( KEY_IMAGES_SERVER ),
+ Setting.of( label( KEY_IMAGES_SERVER ) ),
+ Setting.of( title( KEY_IMAGES_SERVER ),
+ stringProperty( KEY_IMAGES_SERVER ) )
+ )
+ ),
+ Category.of(
+ get( KEY_DEF ),
+ Group.of(
+ get( KEY_DEF_PATH ),
+ Setting.of( label( KEY_DEF_PATH ) ),
+ Setting.of( title( KEY_DEF_PATH ),
+ fileProperty( KEY_DEF_PATH ), false )
+ ),
+ Group.of(
+ get( KEY_DEF_DELIM_BEGAN ),
+ Setting.of( label( KEY_DEF_DELIM_BEGAN ) ),
+ Setting.of( title( KEY_DEF_DELIM_BEGAN ),
+ stringProperty( KEY_DEF_DELIM_BEGAN ) )
+ ),
+ Group.of(
+ get( KEY_DEF_DELIM_ENDED ),
+ Setting.of( label( KEY_DEF_DELIM_ENDED ) ),
+ Setting.of( title( KEY_DEF_DELIM_ENDED ),
+ stringProperty( KEY_DEF_DELIM_ENDED ) )
+ )
+ ),
+ Category.of(
+ get( KEY_UI_FONT ),
+ Group.of(
+ get( KEY_UI_FONT_EDITOR ),
+ Setting.of( label( KEY_UI_FONT_EDITOR_NAME ) ),
+ Setting.of( title( KEY_UI_FONT_EDITOR_NAME ),
+ createFontNameField(
+ stringProperty( KEY_UI_FONT_EDITOR_NAME ),
+ doubleProperty( KEY_UI_FONT_EDITOR_SIZE ) ),
+ stringProperty( KEY_UI_FONT_EDITOR_NAME ) ),
+ Setting.of( label( KEY_UI_FONT_EDITOR_SIZE ) ),
+ Setting.of( title( KEY_UI_FONT_EDITOR_SIZE ),
+ doubleProperty( KEY_UI_FONT_EDITOR_SIZE ) )
+ ),
+ Group.of(
+ get( KEY_UI_FONT_PREVIEW ),
+ Setting.of( label( KEY_UI_FONT_PREVIEW_NAME ) ),
+ Setting.of( title( KEY_UI_FONT_PREVIEW_NAME ),
+ createFontNameField(
+ stringProperty( KEY_UI_FONT_PREVIEW_NAME ),
+ doubleProperty( KEY_UI_FONT_PREVIEW_SIZE ) ),
+ stringProperty( KEY_UI_FONT_PREVIEW_NAME ) ),
+ Setting.of( label( KEY_UI_FONT_PREVIEW_SIZE ) ),
+ Setting.of( title( KEY_UI_FONT_PREVIEW_SIZE ),
+ doubleProperty( KEY_UI_FONT_PREVIEW_SIZE ) ),
+ Setting.of( label( KEY_UI_FONT_PREVIEW_MONO_NAME ) ),
+ Setting.of( title( KEY_UI_FONT_PREVIEW_MONO_NAME ),
+ createFontNameField(
+ stringProperty( KEY_UI_FONT_PREVIEW_MONO_NAME ),
+ doubleProperty( KEY_UI_FONT_PREVIEW_MONO_SIZE ) ),
+ stringProperty( KEY_UI_FONT_PREVIEW_MONO_NAME ) ),
+ Setting.of( label( KEY_UI_FONT_PREVIEW_MONO_SIZE ) ),
+ Setting.of( title( KEY_UI_FONT_PREVIEW_MONO_SIZE ),
+ doubleProperty( KEY_UI_FONT_PREVIEW_MONO_SIZE ) )
+ )
+ ),
+ Category.of(
+ get( KEY_UI_SKIN ),
+ Group.of(
+ get( KEY_UI_SKIN_SELECTION ),
+ Setting.of( label( KEY_UI_SKIN_SELECTION ) ),
+ Setting.of( title( KEY_UI_SKIN_SELECTION ),
+ skinListProperty(),
+ skinProperty( KEY_UI_SKIN_SELECTION ) )
+ ),
+ Group.of(
+ get( KEY_UI_SKIN_CUSTOM ),
+ Setting.of( label( KEY_UI_SKIN_CUSTOM ) ),
+ Setting.of( title( KEY_UI_SKIN_CUSTOM ),
+ fileProperty( KEY_UI_SKIN_CUSTOM ), false )
+ )
+ ),
+ Category.of(
+ get( KEY_UI_PREVIEW ),
+ Group.of(
+ get( KEY_UI_PREVIEW_STYLESHEET ),
+ Setting.of( label( KEY_UI_PREVIEW_STYLESHEET ) ),
+ Setting.of( title( KEY_UI_PREVIEW_STYLESHEET ),
+ fileProperty( KEY_UI_PREVIEW_STYLESHEET ), false )
+ )
+ ),
+ Category.of(
+ get( KEY_LANGUAGE ),
+ Group.of(
+ get( KEY_LANGUAGE_LOCALE ),
+ Setting.of( label( KEY_LANGUAGE_LOCALE ) ),
+ Setting.of( title( KEY_LANGUAGE_LOCALE ),
+ localeListProperty(),
+ localeProperty( KEY_LANGUAGE_LOCALE ) )
+ )
+ )};
+ }
+
+ @SuppressWarnings( "unchecked" )
+ private Setting<StringField, StringProperty> createMultilineSetting(
+ final String description, final Key property ) {
+ final Setting<StringField, StringProperty> setting =
+ Setting.of( description, stringProperty( property ) );
+ final var field = setting.getElement();
+ field.multiline( true );
+
+ return setting;
+ }
+
+ /**
+ * Map ENTER and ESCAPE keys to OK and CANCEL buttons, respectively.
+ */
+ private void initKeyEventHandler( final PreferencesFx preferences ) {
+ final var view = preferences.getView();
+ final var nodes = view.getChildrenUnmodifiable();
+ final var master = (MasterDetailPane) nodes.get( 0 );
+ final var detail = (NavigationView) master.getDetailNode();
+ final var pane = (DialogPane) view.getParent();
+
+ detail.setOnKeyReleased( key -> {
switch( key.getCode() ) {
case ENTER -> ((Button) pane.lookupButton( OK )).fire();
src/main/java/com/keenwrite/preferences/SimpleTableControl.java
+/* Copyright 2021 White Magic Software, Ltd. -- All rights reserved. */
package com.keenwrite.preferences;
import com.dlsc.preferencesfx.formsfx.view.controls.SimpleControl;
import com.keenwrite.ui.table.AltTableCell;
import javafx.beans.property.SimpleObjectProperty;
+import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
-import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.VBox;
import javafx.util.StringConverter;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
-import java.util.Map;
import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import static com.keenwrite.ui.fonts.IconFactory.createGraphic;
import static java.util.Arrays.asList;
import static javafx.collections.FXCollections.observableArrayList;
import static javafx.scene.control.SelectionMode.MULTIPLE;
+import static javafx.scene.control.TableView.CONSTRAINED_RESIZE_POLICY;
public class SimpleTableControl<K, V>
extends SimpleControl<MapField<K, V>, VBox> {
private static long sCounter;
-
- /**
- * Data model for the table view, which must not be immutable.
- */
- private final Map<K, V> mMap;
- public SimpleTableControl( final Map<K, V> map ) {
- assert map != null;
+ private final ObservableList<Entry<K, V>> mModel = observableArrayList();
- mMap = map;
+ public SimpleTableControl() {
}
/**
* {@inheritDoc}
*/
@Override
+ @SuppressWarnings( "unchecked" )
public void initializeParts() {
super.initializeParts();
- final var model = observableArrayList( mMap.entrySet() );
- final var table = new TableView<>( model );
+ final var table = new TableView<>( mModel );
+ table.setColumnResizePolicy( CONSTRAINED_RESIZE_POLICY );
table.setEditable( true );
table.getColumns().addAll(
asList(
createEditableColumnKey( table ),
createEditableColumnValue( table )
)
);
table.getSelectionModel().setSelectionMode( MULTIPLE );
+
+ final var inserted = workaroundBug( table );
final var buttons = new ButtonBar();
buttons.getButtons().addAll(
createButton(
"Add", "PLUS",
event -> {
sCounter++;
- model.add(
+ inserted.set( true );
+ mModel.add(
new SimpleEntry<>(
(K) ("key" + sCounter),
(V) ("value" + sCounter)
)
);
- mMap.clear();
- model.forEach( item -> mMap.put( item.getKey(), item.getValue() ) );
+
+// map.clear();
+// model.forEach( item -> map.put( item.getKey(), item.getValue() ) );
}
),
super.node = vbox;
+ }
+
+ /**
+ * TODO: Delete method when bug is fixed. See the
+ * <a href="https://github.com/dlsc-software-consulting-gmbh/PreferencesFX/issues/413">issue
+ * tracker</a> for details about the bug.
+ *
+ * @param table Add a width listener to correct a slight width change.
+ * @return A Boolean lock so that the bug fix and "Add" button can
+ * be used to ensure regular resizes don't interfere with programmatic ones.
+ */
+ private AtomicBoolean workaroundBug( final TableView<Entry<K, V>> table ) {
+ final var inserted = new AtomicBoolean( true );
+
+ table.widthProperty().addListener( ( c, o, n ) -> {
+ if( (o != null && n != null)
+ && o.intValue() == n.intValue() - 2
+ && inserted.getAndSet( false ) ) {
+ table.setPrefWidth( table.getPrefWidth() - 2 );
+ }
+ } );
+
+ return inserted;
}
@Override
+ @SuppressWarnings( "unchecked" )
public T fromString( final String string ) {
return (T) string;
final var entry = new SimpleEntry<>( key, value );
- mMap.remove( tableEntry.getKey() );
- mMap.put( event.getNewValue(), tableEntry.getValue() );
+ System.out.println( "LA MAP: " + field.mapProperty().get() );
+
+// mMap.remove( tableEntry.getKey() );
+// mMap.put( event.getNewValue(), tableEntry.getValue() );
final var items = event.getTableView().getItems();