From ef9bf1221705aa9fe41847410f5c04ba15c9d87b Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Thu, 22 Feb 2024 14:42:25 +0200 Subject: [PATCH 01/18] added selection box next to the keyword field --- .../jabref/gui/fieldeditors/FieldEditors.java | 2 +- .../gui/fieldeditors/KeywordsEditor.java | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java index e699a398451..022ddde2ba9 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java @@ -96,7 +96,7 @@ public static FieldEditorFX getForField(final Field field, } else if (fieldProperties.contains(FieldProperty.PERSON_NAMES)) { return new PersonsEditor(field, suggestionProvider, preferences, fieldCheckers, isMultiLine, undoManager); } else if (StandardField.KEYWORDS == field) { - return new KeywordsEditor(field, suggestionProvider, fieldCheckers, preferences, undoManager); + return new KeywordsEditor(field, suggestionProvider, fieldCheckers, preferences, undoManager, databaseContext); } else if (field == InternalField.KEY_FIELD) { return new CitationKeyEditor(field, suggestionProvider, fieldCheckers, databaseContext); } else if (fieldProperties.contains(FieldProperty.MARKDOWN)) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java index f11f7d1a362..d110aff9e17 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java @@ -2,23 +2,40 @@ import javax.swing.undo.UndoManager; +import javafx.scene.control.ComboBox; + import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.logic.integrity.FieldCheckers; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.StandardField; import org.jabref.preferences.PreferencesService; public class KeywordsEditor extends SimpleEditor implements FieldEditorFX { + private final BibDatabaseContext databaseContext; + private ComboBox keywordsComboBox; + public KeywordsEditor(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, PreferencesService preferences, - UndoManager undoManager) { + UndoManager undoManager, + BibDatabaseContext databaseContext) { super(field, suggestionProvider, fieldCheckers, preferences, undoManager); + this.databaseContext = databaseContext; + this.keywordsComboBox = createKeywordsComboBox(); + this.getChildren().add(keywordsComboBox); } @Override public double getWeight() { return 2; } + + private ComboBox createKeywordsComboBox() { + ComboBox comboBox = new ComboBox<>(); + comboBox.getItems().addAll(databaseContext.getMetaData().getContentSelectorValuesForField(StandardField.KEYWORDS)); + return comboBox; + } } From 351a0a63c9fd2fd609d9002d18bb43cfd425db90 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Sun, 25 Feb 2024 04:00:27 +0200 Subject: [PATCH 02/18] added selection box next to the keyword field --- .../jabref/gui/fieldeditors/FieldEditors.java | 2 +- .../gui/fieldeditors/KeywordsEditor.fxml | 10 +++ .../gui/fieldeditors/KeywordsEditor.java | 81 +++++++++++++++---- .../fieldeditors/KeywordsEditorViewModel.java | 77 ++++++++++++++++++ 4 files changed, 155 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.fxml create mode 100644 src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java index 022ddde2ba9..665084e3a56 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java @@ -96,7 +96,7 @@ public static FieldEditorFX getForField(final Field field, } else if (fieldProperties.contains(FieldProperty.PERSON_NAMES)) { return new PersonsEditor(field, suggestionProvider, preferences, fieldCheckers, isMultiLine, undoManager); } else if (StandardField.KEYWORDS == field) { - return new KeywordsEditor(field, suggestionProvider, fieldCheckers, preferences, undoManager, databaseContext); + return new KeywordsEditor(field, suggestionProvider, fieldCheckers, databaseContext); } else if (field == InternalField.KEY_FIELD) { return new CitationKeyEditor(field, suggestionProvider, fieldCheckers, databaseContext); } else if (fieldProperties.contains(FieldProperty.MARKDOWN)) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.fxml b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.fxml new file mode 100644 index 00000000000..084bc9b345c --- /dev/null +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.fxml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java index d110aff9e17..ab8f0822ee9 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java @@ -2,30 +2,84 @@ import javax.swing.undo.UndoManager; +import javafx.fxml.FXML; +import javafx.scene.Parent; import javafx.scene.control.ComboBox; +import javafx.scene.layout.HBox; +import org.jabref.gui.DialogService; +import org.jabref.gui.autocompleter.AutoCompletionTextInputBinding; import org.jabref.gui.autocompleter.SuggestionProvider; +import org.jabref.gui.fieldeditors.contextmenu.DefaultMenu; +import org.jabref.gui.util.ViewModelListCellFactory; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.Keyword; import org.jabref.model.entry.field.Field; -import org.jabref.model.entry.field.StandardField; import org.jabref.preferences.PreferencesService; -public class KeywordsEditor extends SimpleEditor implements FieldEditorFX { +import com.airhacks.afterburner.views.ViewLoader; +import jakarta.inject.Inject; - private final BibDatabaseContext databaseContext; - private ComboBox keywordsComboBox; +public class KeywordsEditor extends HBox implements FieldEditorFX { + + @FXML private final KeywordsEditorViewModel viewModel; + @FXML private EditorTextField textField; + @FXML private ComboBox keywordsComboBox; + + @Inject private PreferencesService preferencesService; + @Inject private DialogService dialogService; + @Inject private UndoManager undoManager; public KeywordsEditor(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, - PreferencesService preferences, - UndoManager undoManager, BibDatabaseContext databaseContext) { - super(field, suggestionProvider, fieldCheckers, preferences, undoManager); - this.databaseContext = databaseContext; - this.keywordsComboBox = createKeywordsComboBox(); - this.getChildren().add(keywordsComboBox); + + ViewLoader.view(this) + .root(this) + .load(); + + this.viewModel = new KeywordsEditorViewModel( + field, + suggestionProvider, + fieldCheckers, + preferencesService, + databaseContext, + undoManager, + dialogService); + + textField.textProperty().bindBidirectional(viewModel.textProperty()); + textField.initContextMenu(new DefaultMenu(textField)); + + new ViewModelListCellFactory().withText(Keyword::toString).install(keywordsComboBox); + + AutoCompletionTextInputBinding autoCompleter = AutoCompletionTextInputBinding.autoComplete(textField, viewModel::complete, viewModel.getAutoCompletionStrategy()); + autoCompleter.setShowOnFocus(true); + + new EditorValidator(preferencesService).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textField); + + keywordsComboBox.setItems(viewModel.getFilteredKeywords()); + } + + public KeywordsEditorViewModel getViewModel() { + return viewModel; + } + + @Override + public void bindToEntry(BibEntry entry) { + viewModel.bindToEntry(entry); + } + + @Override + public Parent getNode() { + return this; + } + + @Override + public void requestFocus() { + textField.requestFocus(); } @Override @@ -33,9 +87,8 @@ public double getWeight() { return 2; } - private ComboBox createKeywordsComboBox() { - ComboBox comboBox = new ComboBox<>(); - comboBox.getItems().addAll(databaseContext.getMetaData().getContentSelectorValuesForField(StandardField.KEYWORDS)); - return comboBox; + @FXML + public void addKeyword() { + viewModel.addKeyword(keywordsComboBox.getSelectionModel().getSelectedItem()); } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java new file mode 100644 index 00000000000..ca5022aeed1 --- /dev/null +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java @@ -0,0 +1,77 @@ +package org.jabref.gui.fieldeditors; + +import java.util.stream.Collectors; + +import javax.swing.undo.UndoManager; + +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +import org.jabref.gui.DialogService; +import org.jabref.gui.autocompleter.AppendWordsStrategy; +import org.jabref.gui.autocompleter.AutoCompletionStrategy; +import org.jabref.gui.autocompleter.SuggestionProvider; +import org.jabref.logic.integrity.FieldCheckers; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.Keyword; +import org.jabref.model.entry.KeywordList; +import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.StandardField; +import org.jabref.preferences.PreferencesService; + +public class KeywordsEditorViewModel extends AbstractEditorViewModel { + + private final PreferencesService preferencesService; + private final BibDatabaseContext databaseContext; + private final UndoManager undoManager; + private final DialogService dialogService; + private final Character keywordSeparator; + private final KeywordList contentSelectorKeywords; + private final ObservableList filteredKeywords; + + public KeywordsEditorViewModel(Field field, + SuggestionProvider suggestionProvider, + FieldCheckers fieldCheckers, + PreferencesService preferencesService, + BibDatabaseContext databaseContext, + UndoManager undoManager, + DialogService dialogService) { + + super(field, suggestionProvider, fieldCheckers, undoManager); + this.preferencesService = preferencesService; + this.databaseContext = databaseContext; + this.undoManager = undoManager; + this.dialogService = dialogService; + + keywordSeparator = preferencesService.getBibEntryPreferences().getKeywordSeparator(); + + filteredKeywords = FXCollections.observableArrayList(); + contentSelectorKeywords = new KeywordList(databaseContext.getMetaData().getContentSelectorValuesForField(StandardField.KEYWORDS)); + + textProperty().addListener((observable, oldValue, newValue) -> updateFilteredKeywords(newValue)); + } + + public AutoCompletionStrategy getAutoCompletionStrategy() { + return new AppendWordsStrategy(); + } + + public ObservableList getFilteredKeywords() { + return filteredKeywords; + } + + private void updateFilteredKeywords(String existingKeywords) { + KeywordList parsedKeywords = KeywordList.parse(existingKeywords, keywordSeparator); + filteredKeywords.setAll(contentSelectorKeywords.stream() + .filter(keyword -> !parsedKeywords.contains(keyword)) + .collect(Collectors.toList())); + } + + public void addKeyword(Keyword selectedItem) { + if (selectedItem != null) { + KeywordList currentKeywords = KeywordList.parse(textProperty().getValue(), keywordSeparator); + currentKeywords.add(selectedItem); + Platform.runLater(() -> textProperty().setValue(currentKeywords.getAsString(keywordSeparator))); + } + } +} From 1d449099d10a8802851fda77a3f038a7c643ad74 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Mon, 26 Feb 2024 11:10:03 +0200 Subject: [PATCH 03/18] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9db672a5f8a..739136d9f46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - When pasting HTML into the abstract or a comment field, the hypertext is automatically converted to Markdown. [#10558](https://github.com/JabRef/jabref/issues/10558) - We added the possibility to redownload files that had been present but are no longer in the specified location. [#10848](https://github.com/JabRef/jabref/issues/10848) - We added the citation key pattern `[camelN]`. Equivalent to the first N words of the `[camel]` pattern. +- We added a selection box next to the keywords field, The list is filled with the contents of the content selectors, excluding existing keywords. [#10560](https://github.com/JabRef/jabref/issues/10560) ### Changed From 4173beb383dea2860d3ad325fd0e047d72df4056 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Fri, 1 Mar 2024 02:29:54 +0200 Subject: [PATCH 04/18] Rewrite keywords field: used tags field --- .../jabref/gui/fieldeditors/FieldEditors.java | 2 +- .../gui/fieldeditors/KeywordsEditor.fxml | 8 +- .../gui/fieldeditors/KeywordsEditor.java | 69 +++++++++++------- .../fieldeditors/KeywordsEditorViewModel.java | 73 ++++++------------- .../gui/fieldeditors/LinkedEntriesEditor.java | 2 + 5 files changed, 74 insertions(+), 80 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java index 665084e3a56..ec921b2f523 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java @@ -96,7 +96,7 @@ public static FieldEditorFX getForField(final Field field, } else if (fieldProperties.contains(FieldProperty.PERSON_NAMES)) { return new PersonsEditor(field, suggestionProvider, preferences, fieldCheckers, isMultiLine, undoManager); } else if (StandardField.KEYWORDS == field) { - return new KeywordsEditor(field, suggestionProvider, fieldCheckers, databaseContext); + return new KeywordsEditor(field, suggestionProvider, fieldCheckers); } else if (field == InternalField.KEY_FIELD) { return new CitationKeyEditor(field, suggestionProvider, fieldCheckers, databaseContext); } else if (fieldProperties.contains(FieldProperty.MARKDOWN)) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.fxml b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.fxml index 084bc9b345c..0f96aca52a8 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.fxml +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.fxml @@ -1,10 +1,8 @@ - - + - - + fx:controller="org.jabref.gui.fieldeditors.KeywordsEditor" > + diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java index ab8f0822ee9..878cd54497f 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java @@ -1,41 +1,45 @@ package org.jabref.gui.fieldeditors; +import java.util.Comparator; +import java.util.stream.Collectors; + import javax.swing.undo.UndoManager; +import javafx.beans.binding.Bindings; import javafx.fxml.FXML; +import javafx.scene.Node; import javafx.scene.Parent; -import javafx.scene.control.ComboBox; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.Label; import javafx.scene.layout.HBox; import org.jabref.gui.DialogService; -import org.jabref.gui.autocompleter.AutoCompletionTextInputBinding; import org.jabref.gui.autocompleter.SuggestionProvider; -import org.jabref.gui.fieldeditors.contextmenu.DefaultMenu; +import org.jabref.gui.icon.IconTheme; import org.jabref.gui.util.ViewModelListCellFactory; import org.jabref.logic.integrity.FieldCheckers; -import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.Keyword; import org.jabref.model.entry.field.Field; import org.jabref.preferences.PreferencesService; import com.airhacks.afterburner.views.ViewLoader; +import com.dlsc.gemsfx.TagsField; import jakarta.inject.Inject; public class KeywordsEditor extends HBox implements FieldEditorFX { - @FXML private final KeywordsEditorViewModel viewModel; - @FXML private EditorTextField textField; - @FXML private ComboBox keywordsComboBox; + @FXML private TagsField keywordTagsField; @Inject private PreferencesService preferencesService; @Inject private DialogService dialogService; @Inject private UndoManager undoManager; + private final KeywordsEditorViewModel viewModel; + public KeywordsEditor(Field field, SuggestionProvider suggestionProvider, - FieldCheckers fieldCheckers, - BibDatabaseContext databaseContext) { + FieldCheckers fieldCheckers) { ViewLoader.view(this) .root(this) @@ -46,21 +50,41 @@ public KeywordsEditor(Field field, suggestionProvider, fieldCheckers, preferencesService, - databaseContext, - undoManager, - dialogService); + undoManager); - textField.textProperty().bindBidirectional(viewModel.textProperty()); - textField.initContextMenu(new DefaultMenu(textField)); + keywordTagsField.setCellFactory(new ViewModelListCellFactory().withText(Keyword::toString)); + keywordTagsField.setTagViewFactory(this::createTag); - new ViewModelListCellFactory().withText(Keyword::toString).install(keywordsComboBox); + keywordTagsField.setSuggestionProvider(request -> + suggestionProvider.getPossibleSuggestions().stream() + .filter(suggestion -> suggestion.toString() + .toLowerCase() + .contains(request.getUserText().toLowerCase())) + .map(suggestion -> new Keyword(suggestion.toString())) + .distinct() + .collect(Collectors.toList())); - AutoCompletionTextInputBinding autoCompleter = AutoCompletionTextInputBinding.autoComplete(textField, viewModel::complete, viewModel.getAutoCompletionStrategy()); - autoCompleter.setShowOnFocus(true); + keywordTagsField.setConverter(viewModel.getStringConverter()); + keywordTagsField.setMatcher((keyword, searchText) -> keyword.get().toLowerCase().startsWith(searchText.toLowerCase())); + keywordTagsField.setComparator(Comparator.comparing(Keyword::get)); - new EditorValidator(preferencesService).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textField); + keywordTagsField.setNewItemProducer(searchText -> viewModel.getStringConverter().fromString(searchText)); - keywordsComboBox.setItems(viewModel.getFilteredKeywords()); + keywordTagsField.setPlaceholder(null); + keywordTagsField.setShowSearchIcon(false); + keywordTagsField.getEditor().getStyleClass().clear(); + keywordTagsField.getEditor().getStyleClass().add("tags-field-editor"); + + Bindings.bindContentBidirectional(keywordTagsField.getTags(), viewModel.keywordListProperty()); + } + + private Node createTag(Keyword keyword) { + Label tagLabel = new Label(); + tagLabel.setText(keyword.get()); + tagLabel.setGraphic(IconTheme.JabRefIcons.REMOVE_TAGS.getGraphicNode()); + tagLabel.getGraphic().setOnMouseClicked(event -> keywordTagsField.removeTags(keyword)); + tagLabel.setContentDisplay(ContentDisplay.RIGHT); + return tagLabel; } public KeywordsEditorViewModel getViewModel() { @@ -79,16 +103,11 @@ public Parent getNode() { @Override public void requestFocus() { - textField.requestFocus(); + keywordTagsField.requestFocus(); } @Override public double getWeight() { return 2; } - - @FXML - public void addKeyword() { - viewModel.addKeyword(keywordsComboBox.getSelectionModel().getSelectedItem()); - } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java index ca5022aeed1..78600ee315d 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java @@ -1,77 +1,52 @@ package org.jabref.gui.fieldeditors; -import java.util.stream.Collectors; - import javax.swing.undo.UndoManager; -import javafx.application.Platform; +import javafx.beans.property.ListProperty; +import javafx.beans.property.SimpleListProperty; import javafx.collections.FXCollections; -import javafx.collections.ObservableList; +import javafx.util.StringConverter; -import org.jabref.gui.DialogService; -import org.jabref.gui.autocompleter.AppendWordsStrategy; -import org.jabref.gui.autocompleter.AutoCompletionStrategy; import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.logic.integrity.FieldCheckers; -import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.Keyword; -import org.jabref.model.entry.KeywordList; import org.jabref.model.entry.field.Field; -import org.jabref.model.entry.field.StandardField; import org.jabref.preferences.PreferencesService; public class KeywordsEditorViewModel extends AbstractEditorViewModel { - private final PreferencesService preferencesService; - private final BibDatabaseContext databaseContext; - private final UndoManager undoManager; - private final DialogService dialogService; - private final Character keywordSeparator; - private final KeywordList contentSelectorKeywords; - private final ObservableList filteredKeywords; + private final ListProperty keywordListProperty; public KeywordsEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, PreferencesService preferencesService, - BibDatabaseContext databaseContext, - UndoManager undoManager, - DialogService dialogService) { + UndoManager undoManager) { super(field, suggestionProvider, fieldCheckers, undoManager); - this.preferencesService = preferencesService; - this.databaseContext = databaseContext; - this.undoManager = undoManager; - this.dialogService = dialogService; - - keywordSeparator = preferencesService.getBibEntryPreferences().getKeywordSeparator(); - - filteredKeywords = FXCollections.observableArrayList(); - contentSelectorKeywords = new KeywordList(databaseContext.getMetaData().getContentSelectorValuesForField(StandardField.KEYWORDS)); - - textProperty().addListener((observable, oldValue, newValue) -> updateFilteredKeywords(newValue)); - } - - public AutoCompletionStrategy getAutoCompletionStrategy() { - return new AppendWordsStrategy(); - } - public ObservableList getFilteredKeywords() { - return filteredKeywords; + Character keywordSeparator = preferencesService.getBibEntryPreferences().getKeywordSeparator(); + keywordListProperty = new SimpleListProperty<>(FXCollections.observableArrayList()); } - private void updateFilteredKeywords(String existingKeywords) { - KeywordList parsedKeywords = KeywordList.parse(existingKeywords, keywordSeparator); - filteredKeywords.setAll(contentSelectorKeywords.stream() - .filter(keyword -> !parsedKeywords.contains(keyword)) - .collect(Collectors.toList())); + public ListProperty keywordListProperty() { + return keywordListProperty; } - public void addKeyword(Keyword selectedItem) { - if (selectedItem != null) { - KeywordList currentKeywords = KeywordList.parse(textProperty().getValue(), keywordSeparator); - currentKeywords.add(selectedItem); - Platform.runLater(() -> textProperty().setValue(currentKeywords.getAsString(keywordSeparator))); - } + public StringConverter getStringConverter() { + return new StringConverter<>() { + @Override + public String toString(Keyword keyword) { + if (keyword != null) { + return keyword.get(); + } + return ""; + } + + @Override + public Keyword fromString(String keywordString) { + return new Keyword(keywordString); + } + }; } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java index 3c530084dd3..67e33ae1ecb 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java @@ -71,6 +71,8 @@ public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, Sugg entryLinkField.setNewItemProducer(searchText -> viewModel.getStringConverter().fromString(searchText)); entryLinkField.setMatcher((entryLink, searchText) -> entryLink.getKey().toLowerCase().startsWith(searchText.toLowerCase())); entryLinkField.setComparator(Comparator.comparing(ParsedEntryLink::getKey)); + + entryLinkField.setPlaceholder(null); entryLinkField.setShowSearchIcon(false); entryLinkField.getEditor().getStyleClass().clear(); entryLinkField.getEditor().getStyleClass().add("tags-field-editor"); From 623cf3a12f0693989f31bdd14e07f7c0b24d0c0f Mon Sep 17 00:00:00 2001 From: Loay Ghreeb <52158423+LoayGhreeb@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:49:49 +0200 Subject: [PATCH 05/18] Update src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java Co-authored-by: Oliver Kopp --- .../jabref/gui/fieldeditors/KeywordsEditorViewModel.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java index 78600ee315d..664390d8856 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java @@ -37,10 +37,10 @@ public StringConverter getStringConverter() { return new StringConverter<>() { @Override public String toString(Keyword keyword) { - if (keyword != null) { - return keyword.get(); + if (keyword == null) { + return ""; } - return ""; + return keyword.get(); } @Override From ba7487d0821a18eb385612811ce834763240a1d2 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Sat, 2 Mar 2024 01:14:42 +0200 Subject: [PATCH 06/18] Fix Suggestion Provider [#8145] --- src/main/java/org/jabref/gui/LibraryTab.java | 4 ++-- .../gui/autocompleter/SuggestionProviders.java | 9 --------- .../jabref/gui/fieldeditors/FieldEditors.java | 5 ++--- .../gui/fieldeditors/LinkedEntriesEditor.java | 12 +++--------- .../LinkedEntriesEditorViewModel.java | 16 ++++++++++++++++ 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 8b9488b1559..d97e7456798 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -597,8 +597,8 @@ private void setupAutoCompletion() { if (autoCompletePreferences.shouldAutoComplete()) { suggestionProviders = new SuggestionProviders(getDatabase(), Globals.journalAbbreviationRepository, autoCompletePreferences); } else { - // Create suggestion providers with database for crossref if auto-completion is deactivated - suggestionProviders = new SuggestionProviders(getDatabase()); + // Create empty suggestion providers if auto-completion is deactivated + suggestionProviders = new SuggestionProviders(); } searchAutoCompleter = new PersonNameSuggestionProvider(FieldFactory.getPersonNameFields(), getDatabase()); } diff --git a/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java b/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java index 7d023f4d815..06d2abef8a8 100644 --- a/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java +++ b/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java @@ -22,21 +22,12 @@ public SuggestionProviders(BibDatabase database, JournalAbbreviationRepository a this.isEmpty = false; } - public SuggestionProviders(BibDatabase database) { - this.database = database; - this.isEmpty = true; - } - public SuggestionProviders() { this.isEmpty = true; } public SuggestionProvider getForField(Field field) { if (isEmpty || !autoCompletePreferences.getCompleteFields().contains(field)) { - Set fieldProperties = field.getProperties(); - if (fieldProperties.contains(FieldProperty.SINGLE_ENTRY_LINK)) { - return new BibEntrySuggestionProvider(database); - } return new EmptySuggestionProvider(); } diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java index ec921b2f523..e680a7dc4d1 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java @@ -15,7 +15,6 @@ import org.jabref.logic.integrity.FieldCheckers; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.model.database.BibDatabaseContext; -import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldFactory; import org.jabref.model.entry.field.FieldProperty; @@ -90,9 +89,9 @@ public static FieldEditorFX getForField(final Field field, return new OptionEditor<>(new TypeEditorViewModel(field, suggestionProvider, fieldCheckers, undoManager)); } } else if (fieldProperties.contains(FieldProperty.SINGLE_ENTRY_LINK)) { - return new LinkedEntriesEditor(field, databaseContext, (SuggestionProvider) suggestionProvider, fieldCheckers); + return new LinkedEntriesEditor(field, databaseContext, suggestionProvider, fieldCheckers); } else if (fieldProperties.contains(FieldProperty.MULTIPLE_ENTRY_LINK)) { - return new LinkedEntriesEditor(field, databaseContext, (SuggestionProvider) suggestionProvider, fieldCheckers); + return new LinkedEntriesEditor(field, databaseContext, suggestionProvider, fieldCheckers); } else if (fieldProperties.contains(FieldProperty.PERSON_NAMES)) { return new PersonsEditor(field, suggestionProvider, preferences, fieldCheckers, isMultiLine, undoManager); } else if (StandardField.KEYWORDS == field) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java index 67e33ae1ecb..c3e2daf979e 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java @@ -1,7 +1,6 @@ package org.jabref.gui.fieldeditors; import java.util.Comparator; -import java.util.stream.Collectors; import javax.swing.undo.UndoManager; @@ -51,7 +50,7 @@ public class LinkedEntriesEditor extends HBox implements FieldEditorFX { private final LinkedEntriesEditorViewModel viewModel; - public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { + public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { ViewLoader.view(this) .root(this) .load(); @@ -59,13 +58,8 @@ public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, Sugg this.viewModel = new LinkedEntriesEditorViewModel(field, suggestionProvider, databaseContext, fieldCheckers, undoManager); entryLinkField.setCellFactory(new ViewModelListCellFactory().withText(ParsedEntryLink::getKey)); - // Mind the .collect(Collectors.toList()) as the list needs to be mutable - entryLinkField.setSuggestionProvider(request -> - suggestionProvider.getPossibleSuggestions().stream() - .filter(suggestion -> suggestion.getCitationKey().orElse("").toLowerCase() - .contains(request.getUserText().toLowerCase())) - .map(ParsedEntryLink::new) - .collect(Collectors.toList())); + entryLinkField.setSuggestionProvider(request -> viewModel.getSuggestions(request.getUserText())); + entryLinkField.setTagViewFactory(this::createTag); entryLinkField.setConverter(viewModel.getStringConverter()); entryLinkField.setNewItemProducer(searchText -> viewModel.getStringConverter().fromString(searchText)); diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java index 28048defa97..0c077c67853 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java @@ -1,5 +1,8 @@ package org.jabref.gui.fieldeditors; +import java.util.List; +import java.util.stream.Collectors; + import javax.swing.undo.UndoManager; import javafx.beans.property.ListProperty; @@ -11,6 +14,7 @@ import org.jabref.gui.util.BindingsHelper; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.EntryLinkList; import org.jabref.model.entry.ParsedEntryLink; import org.jabref.model.entry.field.Field; @@ -18,12 +22,15 @@ public class LinkedEntriesEditorViewModel extends AbstractEditorViewModel { private final BibDatabaseContext databaseContext; + private final SuggestionProvider suggestionProvider; private final ListProperty linkedEntries; public LinkedEntriesEditorViewModel(Field field, SuggestionProvider suggestionProvider, BibDatabaseContext databaseContext, FieldCheckers fieldCheckers, UndoManager undoManager) { super(field, suggestionProvider, fieldCheckers, undoManager); this.databaseContext = databaseContext; + this.suggestionProvider = suggestionProvider; + linkedEntries = new SimpleListProperty<>(FXCollections.observableArrayList()); BindingsHelper.bindContentBidirectional( linkedEntries, @@ -53,6 +60,15 @@ public ParsedEntryLink fromString(String key) { }; } + public List getSuggestions(String request) { + return ((List) suggestionProvider.getPossibleSuggestions()) + .stream() + .map(suggestion -> suggestion instanceof BibEntry ? ((BibEntry) suggestion).getCitationKey().orElse("") : (String) suggestion) + .filter(suggestion -> suggestion.toLowerCase().contains(request.toLowerCase())) + .map(suggestion -> new ParsedEntryLink(suggestion, databaseContext.getDatabase())) + .collect(Collectors.toList()); + } + public void jumpToEntry(ParsedEntryLink parsedEntryLink) { // TODO: Implement jump to entry - The implementation can be based on JabRefFrame.jumpToEntry // TODO: Add toolitp for tag: Localization.lang("Jump to entry") From 9022430b16f7b6ce48763706f7f92b6cb116f2d1 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Sat, 2 Mar 2024 01:16:02 +0200 Subject: [PATCH 07/18] Add context menu for keywordsEditor --- src/main/java/org/jabref/gui/Base.css | 8 ++ .../gui/fieldeditors/KeywordsEditor.java | 78 ++++++++++++++----- .../fieldeditors/KeywordsEditorViewModel.java | 13 ++++ 3 files changed, 80 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 8aa35586865..dbd3ef68524 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -1410,6 +1410,14 @@ We want to have a look that matches our icons in the tool-bar */ -fx-border-width: 0; } +.chip-view > .chip-container > .close-icon { + -fx-background-color: transparent; +} + +.chip-view > .chip-container > .label { + -fx-text-fill: -fx-focused-text-base-color; +} + .searchBar:invalid { -fx-background-color: rgba(240, 128, 128, 0.5); } diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java index 878cd54497f..61eb38f7221 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java @@ -1,7 +1,6 @@ package org.jabref.gui.fieldeditors; import java.util.Comparator; -import java.util.stream.Collectors; import javax.swing.undo.UndoManager; @@ -9,31 +8,42 @@ import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.Parent; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.Label; +import javafx.scene.control.ContextMenu; import javafx.scene.layout.HBox; +import org.jabref.gui.ClipBoardManager; import org.jabref.gui.DialogService; +import org.jabref.gui.JabRefDialogService; +import org.jabref.gui.actions.ActionFactory; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.actions.StandardActions; import org.jabref.gui.autocompleter.SuggestionProvider; -import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.util.ViewModelListCellFactory; import org.jabref.logic.integrity.FieldCheckers; +import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.Keyword; import org.jabref.model.entry.field.Field; import org.jabref.preferences.PreferencesService; import com.airhacks.afterburner.views.ViewLoader; +import com.dlsc.gemsfx.ChipView; import com.dlsc.gemsfx.TagsField; import jakarta.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class KeywordsEditor extends HBox implements FieldEditorFX { + private static final Logger LOGGER = LoggerFactory.getLogger(KeywordsEditor.class); @FXML private TagsField keywordTagsField; @Inject private PreferencesService preferencesService; @Inject private DialogService dialogService; @Inject private UndoManager undoManager; + @Inject private ClipBoardManager clipBoardManager; + @Inject private KeyBindingRepository keyBindingRepository; private final KeywordsEditorViewModel viewModel; @@ -55,15 +65,7 @@ public KeywordsEditor(Field field, keywordTagsField.setCellFactory(new ViewModelListCellFactory().withText(Keyword::toString)); keywordTagsField.setTagViewFactory(this::createTag); - keywordTagsField.setSuggestionProvider(request -> - suggestionProvider.getPossibleSuggestions().stream() - .filter(suggestion -> suggestion.toString() - .toLowerCase() - .contains(request.getUserText().toLowerCase())) - .map(suggestion -> new Keyword(suggestion.toString())) - .distinct() - .collect(Collectors.toList())); - + keywordTagsField.setSuggestionProvider(request -> viewModel.getSuggestions(request.getUserText())); keywordTagsField.setConverter(viewModel.getStringConverter()); keywordTagsField.setMatcher((keyword, searchText) -> keyword.get().toLowerCase().startsWith(searchText.toLowerCase())); keywordTagsField.setComparator(Comparator.comparing(Keyword::get)); @@ -79,12 +81,19 @@ public KeywordsEditor(Field field, } private Node createTag(Keyword keyword) { - Label tagLabel = new Label(); - tagLabel.setText(keyword.get()); - tagLabel.setGraphic(IconTheme.JabRefIcons.REMOVE_TAGS.getGraphicNode()); - tagLabel.getGraphic().setOnMouseClicked(event -> keywordTagsField.removeTags(keyword)); - tagLabel.setContentDisplay(ContentDisplay.RIGHT); - return tagLabel; + ChipView chip = new ChipView<>(); + chip.setText(keyword.get()); + chip.setOnClose(event -> keywordTagsField.removeTags(keyword)); + + ContextMenu contextMenu = new ContextMenu(); + ActionFactory factory = new ActionFactory(keyBindingRepository); + contextMenu.getItems().addAll( + factory.createMenuItem(StandardActions.COPY, new KeywordsEditor.TagContextAction(StandardActions.COPY, keyword)), + factory.createMenuItem(StandardActions.CUT, new KeywordsEditor.TagContextAction(StandardActions.CUT, keyword)), + factory.createMenuItem(StandardActions.DELETE, new KeywordsEditor.TagContextAction(StandardActions.DELETE, keyword)) + ); + chip.setContextMenu(contextMenu); + return chip; } public KeywordsEditorViewModel getViewModel() { @@ -110,4 +119,35 @@ public void requestFocus() { public double getWeight() { return 2; } + + private class TagContextAction extends SimpleCommand { + private final StandardActions command; + private final Keyword keyword; + + public TagContextAction(StandardActions command, Keyword keyword) { + this.command = command; + this.keyword = keyword; + } + + @Override + public void execute() { + switch (command) { + case COPY -> { + clipBoardManager.setContent(keyword.get()); + dialogService.notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(keyword.get()))); + } + case CUT -> { + clipBoardManager.setContent(keyword.get()); + dialogService.notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(keyword.get()))); + keywordTagsField.removeTags(keyword); + } + case DELETE -> + keywordTagsField.removeTags(keyword); + default -> + LOGGER.info("Action {} not defined", command.getText()); + } + } + } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java index 664390d8856..9795ef9b6b5 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java @@ -1,5 +1,8 @@ package org.jabref.gui.fieldeditors; +import java.util.List; +import java.util.stream.Collectors; + import javax.swing.undo.UndoManager; import javafx.beans.property.ListProperty; @@ -16,6 +19,7 @@ public class KeywordsEditorViewModel extends AbstractEditorViewModel { private final ListProperty keywordListProperty; + private final SuggestionProvider suggestionProvider; public KeywordsEditorViewModel(Field field, SuggestionProvider suggestionProvider, @@ -25,6 +29,7 @@ public KeywordsEditorViewModel(Field field, super(field, suggestionProvider, fieldCheckers, undoManager); + this.suggestionProvider = suggestionProvider; Character keywordSeparator = preferencesService.getBibEntryPreferences().getKeywordSeparator(); keywordListProperty = new SimpleListProperty<>(FXCollections.observableArrayList()); } @@ -49,4 +54,12 @@ public Keyword fromString(String keywordString) { } }; } + + public List getSuggestions(String request) { + return suggestionProvider.getPossibleSuggestions().stream() + .map(String.class::cast) + .filter(keyword -> keyword.toLowerCase().contains(request.toLowerCase())) + .map(Keyword::new) + .collect(Collectors.toList()); + } } From 21242e66820569fe18c63dea16ac31d5fb48f173 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Sat, 2 Mar 2024 03:12:28 +0200 Subject: [PATCH 08/18] Fixed OpenRewrite test issue --- .../jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java index 0c077c67853..eccefaef03a 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java @@ -63,7 +63,7 @@ public ParsedEntryLink fromString(String key) { public List getSuggestions(String request) { return ((List) suggestionProvider.getPossibleSuggestions()) .stream() - .map(suggestion -> suggestion instanceof BibEntry ? ((BibEntry) suggestion).getCitationKey().orElse("") : (String) suggestion) + .map(suggestion -> suggestion instanceof BibEntry bibEntry ? bibEntry.getCitationKey().orElse("") : (String) suggestion) .filter(suggestion -> suggestion.toLowerCase().contains(request.toLowerCase())) .map(suggestion -> new ParsedEntryLink(suggestion, databaseContext.getDatabase())) .collect(Collectors.toList()); From 96818568a41e84d9dd57e0e4533646c9cb59981c Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Sat, 2 Mar 2024 23:19:57 +0200 Subject: [PATCH 09/18] Add typed text as first suggestion --- .../jabref/gui/fieldeditors/KeywordsEditor.java | 1 - .../fieldeditors/KeywordsEditorViewModel.java | 17 ++++++++++++----- .../gui/fieldeditors/LinkedEntriesEditor.java | 2 -- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java index 61eb38f7221..4d9a7740859 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java @@ -72,7 +72,6 @@ public KeywordsEditor(Field field, keywordTagsField.setNewItemProducer(searchText -> viewModel.getStringConverter().fromString(searchText)); - keywordTagsField.setPlaceholder(null); keywordTagsField.setShowSearchIcon(false); keywordTagsField.getEditor().getStyleClass().clear(); keywordTagsField.getEditor().getStyleClass().add("tags-field-editor"); diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java index 9795ef9b6b5..69b5966763b 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java @@ -56,10 +56,17 @@ public Keyword fromString(String keywordString) { } public List getSuggestions(String request) { - return suggestionProvider.getPossibleSuggestions().stream() - .map(String.class::cast) - .filter(keyword -> keyword.toLowerCase().contains(request.toLowerCase())) - .map(Keyword::new) - .collect(Collectors.toList()); + List suggestions = suggestionProvider.getPossibleSuggestions().stream() + .map(String.class::cast) + .filter(keyword -> keyword.toLowerCase().contains(request.toLowerCase())) + .map(Keyword::new) + .collect(Collectors.toList()); + + Keyword requestedKeyword = new Keyword(request); + if (!suggestions.contains(requestedKeyword)) { + suggestions.addFirst(requestedKeyword); + } + + return suggestions; } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java index c3e2daf979e..c13125727cc 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java @@ -65,8 +65,6 @@ public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, Sugg entryLinkField.setNewItemProducer(searchText -> viewModel.getStringConverter().fromString(searchText)); entryLinkField.setMatcher((entryLink, searchText) -> entryLink.getKey().toLowerCase().startsWith(searchText.toLowerCase())); entryLinkField.setComparator(Comparator.comparing(ParsedEntryLink::getKey)); - - entryLinkField.setPlaceholder(null); entryLinkField.setShowSearchIcon(false); entryLinkField.getEditor().getStyleClass().clear(); entryLinkField.getEditor().getStyleClass().add("tags-field-editor"); From 8d18aa567579a0513f2a3ed69c26d53b746e6424 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Sat, 2 Mar 2024 23:33:57 +0200 Subject: [PATCH 10/18] Revert "Fix Suggestion Provider" --- src/main/java/org/jabref/gui/LibraryTab.java | 4 ++-- .../gui/autocompleter/SuggestionProviders.java | 9 +++++++++ .../org/jabref/gui/fieldeditors/FieldEditors.java | 5 +++-- .../jabref/gui/fieldeditors/LinkedEntriesEditor.java | 12 +++++++++--- .../fieldeditors/LinkedEntriesEditorViewModel.java | 12 ------------ 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index d97e7456798..8b9488b1559 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -597,8 +597,8 @@ private void setupAutoCompletion() { if (autoCompletePreferences.shouldAutoComplete()) { suggestionProviders = new SuggestionProviders(getDatabase(), Globals.journalAbbreviationRepository, autoCompletePreferences); } else { - // Create empty suggestion providers if auto-completion is deactivated - suggestionProviders = new SuggestionProviders(); + // Create suggestion providers with database for crossref if auto-completion is deactivated + suggestionProviders = new SuggestionProviders(getDatabase()); } searchAutoCompleter = new PersonNameSuggestionProvider(FieldFactory.getPersonNameFields(), getDatabase()); } diff --git a/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java b/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java index 06d2abef8a8..7d023f4d815 100644 --- a/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java +++ b/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java @@ -22,12 +22,21 @@ public SuggestionProviders(BibDatabase database, JournalAbbreviationRepository a this.isEmpty = false; } + public SuggestionProviders(BibDatabase database) { + this.database = database; + this.isEmpty = true; + } + public SuggestionProviders() { this.isEmpty = true; } public SuggestionProvider getForField(Field field) { if (isEmpty || !autoCompletePreferences.getCompleteFields().contains(field)) { + Set fieldProperties = field.getProperties(); + if (fieldProperties.contains(FieldProperty.SINGLE_ENTRY_LINK)) { + return new BibEntrySuggestionProvider(database); + } return new EmptySuggestionProvider(); } diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java index e680a7dc4d1..ec921b2f523 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java @@ -15,6 +15,7 @@ import org.jabref.logic.integrity.FieldCheckers; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldFactory; import org.jabref.model.entry.field.FieldProperty; @@ -89,9 +90,9 @@ public static FieldEditorFX getForField(final Field field, return new OptionEditor<>(new TypeEditorViewModel(field, suggestionProvider, fieldCheckers, undoManager)); } } else if (fieldProperties.contains(FieldProperty.SINGLE_ENTRY_LINK)) { - return new LinkedEntriesEditor(field, databaseContext, suggestionProvider, fieldCheckers); + return new LinkedEntriesEditor(field, databaseContext, (SuggestionProvider) suggestionProvider, fieldCheckers); } else if (fieldProperties.contains(FieldProperty.MULTIPLE_ENTRY_LINK)) { - return new LinkedEntriesEditor(field, databaseContext, suggestionProvider, fieldCheckers); + return new LinkedEntriesEditor(field, databaseContext, (SuggestionProvider) suggestionProvider, fieldCheckers); } else if (fieldProperties.contains(FieldProperty.PERSON_NAMES)) { return new PersonsEditor(field, suggestionProvider, preferences, fieldCheckers, isMultiLine, undoManager); } else if (StandardField.KEYWORDS == field) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java index c13125727cc..3c530084dd3 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java @@ -1,6 +1,7 @@ package org.jabref.gui.fieldeditors; import java.util.Comparator; +import java.util.stream.Collectors; import javax.swing.undo.UndoManager; @@ -50,7 +51,7 @@ public class LinkedEntriesEditor extends HBox implements FieldEditorFX { private final LinkedEntriesEditorViewModel viewModel; - public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { + public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { ViewLoader.view(this) .root(this) .load(); @@ -58,8 +59,13 @@ public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, Sugg this.viewModel = new LinkedEntriesEditorViewModel(field, suggestionProvider, databaseContext, fieldCheckers, undoManager); entryLinkField.setCellFactory(new ViewModelListCellFactory().withText(ParsedEntryLink::getKey)); - entryLinkField.setSuggestionProvider(request -> viewModel.getSuggestions(request.getUserText())); - + // Mind the .collect(Collectors.toList()) as the list needs to be mutable + entryLinkField.setSuggestionProvider(request -> + suggestionProvider.getPossibleSuggestions().stream() + .filter(suggestion -> suggestion.getCitationKey().orElse("").toLowerCase() + .contains(request.getUserText().toLowerCase())) + .map(ParsedEntryLink::new) + .collect(Collectors.toList())); entryLinkField.setTagViewFactory(this::createTag); entryLinkField.setConverter(viewModel.getStringConverter()); entryLinkField.setNewItemProducer(searchText -> viewModel.getStringConverter().fromString(searchText)); diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java index eccefaef03a..72e57e45a73 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java @@ -22,15 +22,12 @@ public class LinkedEntriesEditorViewModel extends AbstractEditorViewModel { private final BibDatabaseContext databaseContext; - private final SuggestionProvider suggestionProvider; private final ListProperty linkedEntries; public LinkedEntriesEditorViewModel(Field field, SuggestionProvider suggestionProvider, BibDatabaseContext databaseContext, FieldCheckers fieldCheckers, UndoManager undoManager) { super(field, suggestionProvider, fieldCheckers, undoManager); this.databaseContext = databaseContext; - this.suggestionProvider = suggestionProvider; - linkedEntries = new SimpleListProperty<>(FXCollections.observableArrayList()); BindingsHelper.bindContentBidirectional( linkedEntries, @@ -60,15 +57,6 @@ public ParsedEntryLink fromString(String key) { }; } - public List getSuggestions(String request) { - return ((List) suggestionProvider.getPossibleSuggestions()) - .stream() - .map(suggestion -> suggestion instanceof BibEntry bibEntry ? bibEntry.getCitationKey().orElse("") : (String) suggestion) - .filter(suggestion -> suggestion.toLowerCase().contains(request.toLowerCase())) - .map(suggestion -> new ParsedEntryLink(suggestion, databaseContext.getDatabase())) - .collect(Collectors.toList()); - } - public void jumpToEntry(ParsedEntryLink parsedEntryLink) { // TODO: Implement jump to entry - The implementation can be based on JabRefFrame.jumpToEntry // TODO: Add toolitp for tag: Localization.lang("Jump to entry") From 28df1865843749c9aceffc1333f4a9b5919c94e9 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Sat, 2 Mar 2024 23:37:24 +0200 Subject: [PATCH 11/18] Remove unused imports --- .../jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java index 72e57e45a73..28048defa97 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java @@ -1,8 +1,5 @@ package org.jabref.gui.fieldeditors; -import java.util.List; -import java.util.stream.Collectors; - import javax.swing.undo.UndoManager; import javafx.beans.property.ListProperty; @@ -14,7 +11,6 @@ import org.jabref.gui.util.BindingsHelper; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.model.database.BibDatabaseContext; -import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.EntryLinkList; import org.jabref.model.entry.ParsedEntryLink; import org.jabref.model.entry.field.Field; From ad0bb947a859ea164c0b1f503362adf978ea6c43 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Sun, 3 Mar 2024 02:17:05 +0200 Subject: [PATCH 12/18] Bind the keywords and tags field --- .../fieldeditors/KeywordsEditorViewModel.java | 21 +++++++++++++++++-- .../org/jabref/model/entry/KeywordList.java | 4 ++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java index 69b5966763b..d2cd2e902da 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java @@ -11,14 +11,17 @@ import javafx.util.StringConverter; import org.jabref.gui.autocompleter.SuggestionProvider; +import org.jabref.gui.util.BindingsHelper; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.model.entry.Keyword; +import org.jabref.model.entry.KeywordList; import org.jabref.model.entry.field.Field; import org.jabref.preferences.PreferencesService; public class KeywordsEditorViewModel extends AbstractEditorViewModel { private final ListProperty keywordListProperty; + private final Character keywordSeparator; private final SuggestionProvider suggestionProvider; public KeywordsEditorViewModel(Field field, @@ -29,9 +32,23 @@ public KeywordsEditorViewModel(Field field, super(field, suggestionProvider, fieldCheckers, undoManager); - this.suggestionProvider = suggestionProvider; - Character keywordSeparator = preferencesService.getBibEntryPreferences().getKeywordSeparator(); keywordListProperty = new SimpleListProperty<>(FXCollections.observableArrayList()); + this.keywordSeparator = preferencesService.getBibEntryPreferences().getKeywordSeparator(); + this.suggestionProvider = suggestionProvider; + + BindingsHelper.bindContentBidirectional( + keywordListProperty, + text, + this::serializeKeywords, + this::parseKeywords); + } + + private String serializeKeywords(List keywords) { + return KeywordList.serialize(keywords, keywordSeparator); + } + + private List parseKeywords(String newText) { + return KeywordList.parse(newText, keywordSeparator).stream().toList(); } public ListProperty keywordListProperty() { diff --git a/src/main/java/org/jabref/model/entry/KeywordList.java b/src/main/java/org/jabref/model/entry/KeywordList.java index d54de57f977..76adb9add8d 100644 --- a/src/main/java/org/jabref/model/entry/KeywordList.java +++ b/src/main/java/org/jabref/model/entry/KeywordList.java @@ -73,6 +73,10 @@ public static KeywordList parse(String keywordString, Character delimiter) { return parse(keywordString, delimiter, Keyword.DEFAULT_HIERARCHICAL_DELIMITER); } + public static String serialize(List keywords, Character delimiter) { + return keywords.stream().map(Keyword::get).collect(Collectors.joining(delimiter.toString())); + } + public static KeywordList merge(String keywordStringA, String keywordStringB, Character delimiter) { KeywordList keywordListA = parse(keywordStringA, delimiter); KeywordList keywordListB = parse(keywordStringB, delimiter); From 7348ad4465bf336b3e7f03832eecfb6c4470918d Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Sun, 3 Mar 2024 02:46:02 +0200 Subject: [PATCH 13/18] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a372a49c163..5e660fb411e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,6 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - When pasting HTML into the abstract or a comment field, the hypertext is automatically converted to Markdown. [#10558](https://github.com/JabRef/jabref/issues/10558) - We added the possibility to redownload files that had been present but are no longer in the specified location. [#10848](https://github.com/JabRef/jabref/issues/10848) - We added the citation key pattern `[camelN]`. Equivalent to the first N words of the `[camel]` pattern. -- We added a selection box next to the keywords field, The list is filled with the contents of the content selectors, excluding existing keywords. [#10560](https://github.com/JabRef/jabref/issues/10560) - We added ability to export in CFF (Citation File Format) [#10661](https://github.com/JabRef/jabref/issues/10661). ### Changed @@ -39,6 +38,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We made the command "Push to TexShop" more robust to allow cite commands with a character before the first slash. [forum#2699](https://discourse.jabref.org/t/push-to-texshop-mac/2699/17?u=siedlerchr) - We only show the notification "Saving library..." if the library contains more than 2000 entries. [#9803](https://github.com/JabRef/jabref/issues/9803) - We enhanced the dialog for adding new fields in the content selector with a selection box containing a list of standard fields. [#10912](https://github.com/JabRef/jabref/pull/10912) +- Keywords filed are now displayed as tags (chips). [#10910](https://github.com/JabRef/jabref/pull/10910) ### Fixed From efb1a1400e7e123236bdff4ab08fc0b546800e29 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Sun, 3 Mar 2024 03:45:45 +0200 Subject: [PATCH 14/18] Remove duplicates from the suggestions list --- .../org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java index d2cd2e902da..3ffb0bc19d9 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java @@ -77,6 +77,7 @@ public List getSuggestions(String request) { .map(String.class::cast) .filter(keyword -> keyword.toLowerCase().contains(request.toLowerCase())) .map(Keyword::new) + .distinct() .collect(Collectors.toList()); Keyword requestedKeyword = new Keyword(request); From f26ce6fb2fbc8b55c1afc55394b2a250cfcbc6e0 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Tue, 5 Mar 2024 00:05:12 +0200 Subject: [PATCH 15/18] use Labels as a tag view --- .../gui/fieldeditors/KeywordsEditor.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java index 4d9a7740859..2d79d22f0db 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditor.java @@ -8,7 +8,9 @@ import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.Parent; +import javafx.scene.control.ContentDisplay; import javafx.scene.control.ContextMenu; +import javafx.scene.control.Label; import javafx.scene.layout.HBox; import org.jabref.gui.ClipBoardManager; @@ -18,6 +20,7 @@ import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.actions.StandardActions; import org.jabref.gui.autocompleter.SuggestionProvider; +import org.jabref.gui.icon.IconTheme; import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.util.ViewModelListCellFactory; import org.jabref.logic.integrity.FieldCheckers; @@ -28,7 +31,6 @@ import org.jabref.preferences.PreferencesService; import com.airhacks.afterburner.views.ViewLoader; -import com.dlsc.gemsfx.ChipView; import com.dlsc.gemsfx.TagsField; import jakarta.inject.Inject; import org.slf4j.Logger; @@ -62,7 +64,7 @@ public KeywordsEditor(Field field, preferencesService, undoManager); - keywordTagsField.setCellFactory(new ViewModelListCellFactory().withText(Keyword::toString)); + keywordTagsField.setCellFactory(new ViewModelListCellFactory().withText(Keyword::get)); keywordTagsField.setTagViewFactory(this::createTag); keywordTagsField.setSuggestionProvider(request -> viewModel.getSuggestions(request.getUserText())); @@ -80,10 +82,11 @@ public KeywordsEditor(Field field, } private Node createTag(Keyword keyword) { - ChipView chip = new ChipView<>(); - chip.setText(keyword.get()); - chip.setOnClose(event -> keywordTagsField.removeTags(keyword)); - + Label tagLabel = new Label(); + tagLabel.setText(keywordTagsField.getConverter().toString(keyword)); + tagLabel.setGraphic(IconTheme.JabRefIcons.REMOVE_TAGS.getGraphicNode()); + tagLabel.getGraphic().setOnMouseClicked(event -> keywordTagsField.removeTags(keyword)); + tagLabel.setContentDisplay(ContentDisplay.RIGHT); ContextMenu contextMenu = new ContextMenu(); ActionFactory factory = new ActionFactory(keyBindingRepository); contextMenu.getItems().addAll( @@ -91,8 +94,8 @@ private Node createTag(Keyword keyword) { factory.createMenuItem(StandardActions.CUT, new KeywordsEditor.TagContextAction(StandardActions.CUT, keyword)), factory.createMenuItem(StandardActions.DELETE, new KeywordsEditor.TagContextAction(StandardActions.DELETE, keyword)) ); - chip.setContextMenu(contextMenu); - return chip; + tagLabel.setContextMenu(contextMenu); + return tagLabel; } public KeywordsEditorViewModel getViewModel() { From 53880bccd8b45cc8d2d71136605d8b752119e67f Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Tue, 5 Mar 2024 00:10:08 +0200 Subject: [PATCH 16/18] Add LOGGER.debug --- .../org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java index 3ffb0bc19d9..8accde67d6d 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/KeywordsEditorViewModel.java @@ -18,6 +18,8 @@ import org.jabref.model.entry.field.Field; import org.jabref.preferences.PreferencesService; +import org.tinylog.Logger; + public class KeywordsEditorViewModel extends AbstractEditorViewModel { private final ListProperty keywordListProperty; @@ -60,6 +62,7 @@ public StringConverter getStringConverter() { @Override public String toString(Keyword keyword) { if (keyword == null) { + Logger.debug("Keyword is null"); return ""; } return keyword.get(); From d16f77c466c69f8ce8381deb129f94603984b4e3 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Tue, 5 Mar 2024 00:19:29 +0200 Subject: [PATCH 17/18] remove chip-view from CSS file --- src/main/java/org/jabref/gui/Base.css | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index dbd3ef68524..8aa35586865 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -1410,14 +1410,6 @@ We want to have a look that matches our icons in the tool-bar */ -fx-border-width: 0; } -.chip-view > .chip-container > .close-icon { - -fx-background-color: transparent; -} - -.chip-view > .chip-container > .label { - -fx-text-fill: -fx-focused-text-base-color; -} - .searchBar:invalid { -fx-background-color: rgba(240, 128, 128, 0.5); } From c4d7d406bcb59b03a56c6b38cadf486866eb60c0 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb Date: Tue, 5 Mar 2024 00:59:23 +0200 Subject: [PATCH 18/18] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e660fb411e..33857b0aa1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We made the command "Push to TexShop" more robust to allow cite commands with a character before the first slash. [forum#2699](https://discourse.jabref.org/t/push-to-texshop-mac/2699/17?u=siedlerchr) - We only show the notification "Saving library..." if the library contains more than 2000 entries. [#9803](https://github.com/JabRef/jabref/issues/9803) - We enhanced the dialog for adding new fields in the content selector with a selection box containing a list of standard fields. [#10912](https://github.com/JabRef/jabref/pull/10912) -- Keywords filed are now displayed as tags (chips). [#10910](https://github.com/JabRef/jabref/pull/10910) +- Keywords filed are now displayed as tags. [#10910](https://github.com/JabRef/jabref/pull/10910) ### Fixed