Skip to content

Commit ceb4e48

Browse files
MemeelAloisHasNeuronsSiedlerchr
authored
Replaces the standard ComboBox with a SearchableComboBox and a free text field in custom Entry Types (#14189)
* create branch * preferences: Make "Add new field" searchable in Custom Entry Types Replaces the standard ComboBox with a SearchableComboBox to improve usability when adding fields. Fixes #14082 * docs: Update changelog for issue 14082 * fix(config): Restore accidentally deleted line * preferences: Split 'Add new field' into ComboBox and TextField Replaces the single editable SearchableComboBox with two separate components: a SearchableComboBox for existing fields and a new TextField for custom fields * Add missing localization keys for custom entry types * moved CHANGELOG to the new unreleased section * revert changes * Remove commented out code --------- Co-authored-by: Aloïs VINCENT <[email protected]> Co-authored-by: Christoph <[email protected]>
1 parent 437c12e commit ceb4e48

File tree

5 files changed

+87
-17
lines changed

5 files changed

+87
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
1717

1818
### Changed
1919

20+
- We replaced the standard ComboBox with a SearchableComboBox and added a free text field in custom Entry Types [#14082](https://github.com/JabRef/jabref/issues/14082)
2021
- We separated the "Clean up entries" dialog into three tabs for clarity [#13819](https://github.com/JabRef/jabref/issues/13819)
2122
- `JabKit`: `--porcelain` does not output any logs to the console anymore. [#14244](https://github.com/JabRef/jabref/pull/14244)
2223
- <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>L</kbd> now opens the terminal in the active library directory. [#14130](https://github.com/JabRef/jabref/issues/14130)

jabgui/src/main/java/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTab.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import javafx.fxml.FXML;
77
import javafx.scene.Group;
88
import javafx.scene.control.Button;
9-
import javafx.scene.control.ComboBox;
109
import javafx.scene.control.Label;
1110
import javafx.scene.control.TableColumn;
1211
import javafx.scene.control.TableRow;
@@ -40,21 +39,24 @@
4039
import com.tobiasdiez.easybind.EasyBind;
4140
import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer;
4241
import jakarta.inject.Inject;
42+
import org.controlsfx.control.SearchableComboBox;
4343

4444
public class CustomEntryTypesTab extends AbstractPreferenceTabView<CustomEntryTypesTabViewModel> implements PreferencesTab {
4545

4646
@FXML private TableView<EntryTypeViewModel> entryTypesTable;
4747
@FXML private TableColumn<EntryTypeViewModel, String> entryTypColumn;
4848
@FXML private TableColumn<EntryTypeViewModel, String> entryTypeActionsColumn;
4949
@FXML private TextField addNewEntryType;
50+
@FXML private TextField addNewCustomFieldText;
5051
@FXML private TableView<FieldViewModel> fields;
5152
@FXML private TableColumn<FieldViewModel, String> fieldNameColumn;
5253
@FXML private TableColumn<FieldViewModel, Boolean> fieldTypeColumn;
5354
@FXML private TableColumn<FieldViewModel, String> fieldTypeActionColumn;
5455
@FXML private TableColumn<FieldViewModel, Boolean> fieldTypeMultilineColumn;
55-
@FXML private ComboBox<Field> addNewField;
56+
@FXML private SearchableComboBox<Field> addNewField;
5657
@FXML private Button addNewEntryTypeButton;
5758
@FXML private Button addNewFieldButton;
59+
@FXML private Button addNewCustomFieldButton;
5860

5961
@Inject private StateManager stateManager;
6062

@@ -89,9 +91,13 @@ public void initialize() {
8991
addNewEntryTypeButton.disableProperty().bind(viewModel.entryTypeValidationStatus().validProperty().not());
9092
addNewFieldButton.disableProperty().bind(viewModel.fieldValidationStatus().validProperty().not().or(viewModel.selectedEntryTypeProperty().isNull()));
9193

94+
viewModel.newCustomFieldToAddProperty().bindBidirectional(addNewCustomFieldText.textProperty());
95+
addNewCustomFieldButton.disableProperty().bind(viewModel.customFieldValidationStatus().validProperty().not().or(viewModel.selectedEntryTypeProperty().isNull()));
96+
9297
Platform.runLater(() -> {
9398
visualizer.initVisualization(viewModel.entryTypeValidationStatus(), addNewEntryType, true);
9499
visualizer.initVisualization(viewModel.fieldValidationStatus(), addNewField, true);
100+
visualizer.initVisualization(viewModel.customFieldValidationStatus(), addNewCustomFieldText, true);
95101
});
96102
}
97103

@@ -203,7 +209,6 @@ private void setupFieldsTable() {
203209
viewModel.newFieldToAddProperty().bindBidirectional(addNewField.valueProperty());
204210
// The valueProperty() of addNewField ComboBox needs to be updated by typing text in the ComboBox textfield,
205211
// since the enabled/disabled state of addNewFieldButton won't update otherwise
206-
EasyBind.subscribe(addNewField.getEditor().textProperty(), text -> addNewField.setValue(FieldsUtil.FIELD_STRING_CONVERTER.fromString(text)));
207212
}
208213

209214
private void makeRotatedColumnHeader(TableColumn<?, ?> column, String text) {
@@ -269,6 +274,11 @@ void addNewField() {
269274
viewModel.addNewField();
270275
}
271276

277+
@FXML
278+
void addNewCustomField() {
279+
viewModel.addNewCustomField();
280+
}
281+
272282
@FXML
273283
void resetEntryTypes() {
274284
boolean reset = dialogService.showConfirmationDialogAndWait(

jabgui/src/main/java/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTabViewModel.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.jabref.model.entry.field.FieldProperty;
3232
import org.jabref.model.entry.field.FieldTextMapper;
3333
import org.jabref.model.entry.field.OrFields;
34+
import org.jabref.model.entry.field.UnknownField;
3435
import org.jabref.model.entry.types.EntryType;
3536
import org.jabref.model.entry.types.UnknownEntryType;
3637

@@ -45,6 +46,7 @@ public class CustomEntryTypesTabViewModel implements PreferenceTabViewModel {
4546
private final ObjectProperty<EntryTypeViewModel> selectedEntryType = new SimpleObjectProperty<>();
4647
private final StringProperty entryTypeToAdd = new SimpleStringProperty("");
4748
private final ObjectProperty<Field> newFieldToAdd = new SimpleObjectProperty<>();
49+
private final StringProperty newCustomFieldToAdd = new SimpleStringProperty("");
4850
private final ObservableList<EntryTypeViewModel> entryTypesWithFields = FXCollections.observableArrayList(extractor -> new Observable[] {extractor.entryType(), extractor.fields()});
4951
private final List<BibEntryType> entryTypesToDelete = new ArrayList<>();
5052

@@ -55,6 +57,7 @@ public class CustomEntryTypesTabViewModel implements PreferenceTabViewModel {
5557

5658
private final Validator entryTypeValidator;
5759
private final Validator fieldValidator;
60+
private final Validator customFieldValidator;
5861
private final Set<Field> multiLineFields = new HashSet<>();
5962

6063
Predicate<Field> isMultiline = field -> this.multiLineFields.contains(field) || field.getProperties().contains(FieldProperty.MULTILINE_TEXT);
@@ -78,6 +81,11 @@ public CustomEntryTypesTabViewModel(BibDatabaseMode mode,
7881
newFieldToAdd,
7982
input -> (input != null) && StringUtil.isNotBlank(FieldTextMapper.getDisplayName(input)),
8083
ValidationMessage.error(Localization.lang("Field cannot be empty. Please enter a name.")));
84+
customFieldValidator = new FunctionBasedValidator<>(
85+
newCustomFieldToAdd,
86+
input -> StringUtil.isNotBlank(input) && !input.contains(" "),
87+
ValidationMessage.error(Localization.lang("Field cannot be empty and must not contain spaces."))
88+
);
8189
}
8290

8391
@Override
@@ -167,6 +175,26 @@ public void addNewField() {
167175
newFieldToAddProperty().setValue(null);
168176
}
169177

178+
public void addNewCustomField() {
179+
String fieldName = newCustomFieldToAdd.get().trim();
180+
Field newField = new UnknownField(fieldName);
181+
182+
boolean fieldExists = displayNameExists(FieldTextMapper.getDisplayName(newField));
183+
184+
if (fieldExists) {
185+
dialogService.showWarningDialogAndWait(
186+
Localization.lang("Duplicate fields"),
187+
Localization.lang("Warning: You added field \"%0\" twice. Only one will be kept.", FieldTextMapper.getDisplayName(newField)));
188+
} else {
189+
this.selectedEntryType.getValue().addField(new FieldViewModel(
190+
newField,
191+
FieldViewModel.Mandatory.REQUIRED,
192+
FieldPriority.IMPORTANT,
193+
false));
194+
}
195+
newCustomFieldToAdd.set("");
196+
}
197+
170198
public boolean displayNameExists(String displayName) {
171199
ObservableList<FieldViewModel> entryFields = this.selectedEntryType.getValue().fields();
172200
return entryFields.stream().anyMatch(fieldViewModel ->
@@ -202,11 +230,19 @@ public ObservableList<Field> fieldsForAdding() {
202230
return this.fieldsForAdding;
203231
}
204232

233+
public StringProperty newCustomFieldToAddProperty() {
234+
return this.newCustomFieldToAdd;
235+
}
236+
205237
public ValidationStatus entryTypeValidationStatus() {
206238
return entryTypeValidator.getValidationStatus();
207239
}
208240

209241
public ValidationStatus fieldValidationStatus() {
210242
return fieldValidator.getValidationStatus();
211243
}
244+
245+
public ValidationStatus customFieldValidationStatus() {
246+
return customFieldValidator.getValidationStatus();
247+
}
212248
}

jabgui/src/main/resources/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTab.fxml

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22

33
<?import javafx.scene.control.Button?>
4-
<?import javafx.scene.control.ComboBox?>
54
<?import javafx.scene.control.Label?>
65
<?import javafx.scene.control.TableColumn?>
76
<?import javafx.scene.control.TableView?>
@@ -11,6 +10,7 @@
1110
<?import javafx.scene.layout.Region?>
1211
<?import javafx.scene.layout.VBox?>
1312
<?import org.jabref.gui.icon.JabRefIconView?>
13+
<?import org.controlsfx.control.SearchableComboBox?>
1414
<fx:root spacing="10.0" type="VBox"
1515
xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
1616
fx:controller="org.jabref.gui.preferences.customentrytypes.CustomEntryTypesTab">
@@ -30,7 +30,7 @@
3030
</columnResizePolicy>
3131
</TableView>
3232
<HBox spacing="10.0">
33-
<TextField fx:id="addNewEntryType"/>
33+
<TextField fx:id="addNewEntryType" promptText="Type new entry type..."/>
3434
<Button fx:id="addNewEntryTypeButton"
3535
prefHeight="20.0" prefWidth="20.0"
3636
styleClass="icon-button,narrow" onAction="#addEntryType">
@@ -60,18 +60,37 @@
6060
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
6161
</columnResizePolicy>
6262
</TableView>
63-
<HBox spacing="10.0" alignment="BASELINE_CENTER">
64-
<ComboBox fx:id="addNewField" editable="true" prefWidth="150.0"/>
65-
<Button fx:id="addNewFieldButton"
66-
prefHeight="20.0" prefWidth="20.0"
67-
styleClass="icon-button,narrow" onAction="#addNewField">
68-
<graphic>
69-
<JabRefIconView glyph="ADD_NOBOX"/>
70-
</graphic>
71-
<tooltip>
72-
<Tooltip text="%Add new Field"/>
73-
</tooltip>
74-
</Button>
63+
<HBox spacing="10.0" alignment="BASELINE_LEFT">
64+
<VBox spacing="5.0">
65+
<HBox spacing="10.0" alignment="BASELINE_CENTER">
66+
<SearchableComboBox fx:id="addNewField" editable="true" prefWidth="150.0"/>
67+
<Button fx:id="addNewFieldButton"
68+
prefHeight="20.0" prefWidth="20.0"
69+
styleClass="icon-button,narrow" onAction="#addNewField">
70+
<graphic>
71+
<JabRefIconView glyph="ADD_NOBOX"/>
72+
</graphic>
73+
<tooltip>
74+
<Tooltip text="%Add new Field"/>
75+
</tooltip>
76+
</Button>
77+
</HBox>
78+
79+
<HBox spacing="10.0" alignment="BASELINE_CENTER">
80+
<TextField fx:id="addNewCustomFieldText" prefWidth="150.0" promptText="%Type new custom field..."/>
81+
<Button fx:id="addNewCustomFieldButton"
82+
prefHeight="20.0" prefWidth="20.0"
83+
styleClass="icon-button,narrow" onAction="#addNewCustomField">
84+
<graphic>
85+
<JabRefIconView glyph="ADD_NOBOX"/>
86+
</graphic>
87+
<tooltip>
88+
<Tooltip text="%Add new custom field"/>
89+
</tooltip>
90+
</Button>
91+
</HBox>
92+
</VBox>
93+
7594
<Region HBox.hgrow="ALWAYS" />
7695
<Button text="%Reset to default" onAction="#resetEntryTypes">
7796
<graphic>

jablib/src/main/resources/l10n/JabRef_en.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2281,6 +2281,10 @@ Required=Required
22812281
Entry\ type\ cannot\ be\ empty\ and\ must\ not\ contain\ spaces.=Entry type cannot be empty and must not contain spaces.
22822282
Field\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Field cannot be empty. Please enter a name.
22832283
2284+
Add\ new\ custom\ field=Add new custom field
2285+
Field\ cannot\ be\ empty\ and\ must\ not\ contain\ spaces.=Field cannot be empty and must not contain spaces.
2286+
Type\ new\ custom\ field...=Type new custom field...
2287+
22842288
Capitalize\ current\ word=Capitalize current word
22852289
Delete\ text=Delete text
22862290
Make\ current\ word\ lowercase=Make current word lowercase

0 commit comments

Comments
 (0)