Skip to content

Commit d8ebffc

Browse files
Fix context menu of the search bar (#11446)
* Fix selecting group triggers in all open libraries * Change color of the search modifier buttons for dark theme #8963 (comment) * Add shortcut to open global search window "Ctrl+shift+F" * fix keep on top subscription * Fix context menu of the search bar * Store preview divider position in global search window Remove constructor * prevent NPE * Fix update search query triggers in all open libraries * Adapt GUI test * Clear search highlight in SourceTab after resetting search query * Revert "Fix update search query triggers in all open libraries" This reverts commit d6d455c. * Revert "Fix selecting group triggers in all open libraries" This reverts commit b0017fb. * Remove regexValidator * listen for changes for search flags --------- Co-authored-by: Siedlerchr <[email protected]>
1 parent fed6bde commit d8ebffc

17 files changed

+135
-151
lines changed

src/main/java/org/jabref/gui/Base.css

+6-10
Original file line numberDiff line numberDiff line change
@@ -1072,29 +1072,25 @@ TextFlow > .tooltip-text-monospaced {
10721072
}
10731073

10741074
/* search modifier buttons */
1075-
.mainToolbar .search-field .toggle-button:selected {
1075+
.global-search-bar .toggle-button:selected {
10761076
-fx-background-color: transparent;
10771077
}
10781078

1079-
.mainToolbar .search-field .toggle-button:hover,
1080-
.mainToolbar .search-field .toggle-button:selected:hover {
1079+
.global-search-bar .toggle-button:hover,
1080+
.global-search-bar .toggle-button:selected:hover {
10811081
-fx-background-color: -jr-icon-background-active;
10821082
}
10831083

1084-
.mainToolbar .search-field .toggle-button .glyph-icon {
1085-
-fx-fill: derive(-jr-search-text, 80%);
1086-
-fx-text-fill: derive(-jr-search-text, 80%);
1084+
.global-search-bar .toggle-button .glyph-icon {
10871085
-fx-icon-color: derive(-jr-search-text, 80%);
10881086
}
10891087

1090-
.mainToolbar .search-field .toggle-button:selected .glyph-icon {
1091-
-fx-fill: -jr-search-text;
1092-
-fx-text-fill: -jr-search-text;
1088+
.global-search-bar .toggle-button:selected .glyph-icon {
10931089
-fx-icon-color: -jr-search-text;
10941090
}
10951091

10961092
/* search text */
1097-
.mainToolbar .search-field .label {
1093+
.global-search-bar .label {
10981094
-fx-padding: 0em 1.8em 0em 0em;
10991095
}
11001096

src/main/java/org/jabref/gui/Dark.css

+2-16
Original file line numberDiff line numberDiff line change
@@ -143,22 +143,8 @@
143143
-fx-background-color: -jr-background;
144144
}
145145

146-
.mainToolbar .search-field .button .glyph-icon {
147-
-fx-fill: derive(-fx-light-text-color, 80%);
148-
-fx-text-fill: derive(-fx-light-text-color, 80%);
149-
-fx-icon-color: derive(-fx-light-text-color, 80%);
150-
}
151-
152-
.mainToolbar .search-field .toggle-button .glyph-icon {
153-
-fx-fill: -jr-search-text;
154-
-fx-text-fill: -jr-search-text;
155-
-fx-icon-color:-jr-search-text;
156-
}
157-
158-
.mainToolbar .search-field .toggle-button:selected .glyph-icon {
159-
-fx-fill: derive(-fx-light-text-color, 80%);
160-
-fx-text-fill: derive(-fx-light-text-color, 80%);
161-
-fx-icon-color: derive(-fx-light-text-color, 80%);
146+
.global-search-bar .toggle-button .glyph-icon {
147+
-fx-icon-color: derive(-jr-search-background, 50%);
162148
}
163149

164150
.notification-bar > .pane {

src/main/java/org/jabref/gui/entryeditor/SourceTab.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,14 @@ public SourceTab(BibDatabaseContext bibDatabaseContext,
131131
}
132132

133133
private void highlightSearchPattern() {
134-
if (searchHighlightPattern.isPresent() && (codeArea != null)) {
134+
if (codeArea != null) {
135135
codeArea.setStyleClass(0, codeArea.getLength(), "text");
136-
Matcher matcher = searchHighlightPattern.get().matcher(codeArea.getText());
137-
while (matcher.find()) {
138-
for (int i = 0; i <= matcher.groupCount(); i++) {
139-
codeArea.setStyleClass(matcher.start(), matcher.end(), "search");
136+
if (searchHighlightPattern.isPresent()) {
137+
Matcher matcher = searchHighlightPattern.get().matcher(codeArea.getText());
138+
while (matcher.find()) {
139+
for (int i = 0; i <= matcher.groupCount(); i++) {
140+
codeArea.setStyleClass(matcher.start(), matcher.end(), "search");
141+
}
140142
}
141143
}
142144
}

src/main/java/org/jabref/gui/fieldeditors/contextmenu/EditorContextAction.java

+6
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,17 @@ public EditorContextAction(StandardActions command, TextInputControl textInputCo
3535
BooleanBinding hasSelectionBinding = Bindings.createBooleanBinding(() -> textInputControl.getSelection().getLength() > 0, textInputControl.selectionProperty());
3636
BooleanBinding allSelectedBinding = Bindings.createBooleanBinding(() -> textInputControl.getSelection().getLength() == textInputControl.getLength());
3737
BooleanBinding maskTextBinding = (BooleanBinding) BindingsHelper.constantOf(textInputControl instanceof PasswordField); // (maskText("A") != "A");
38+
BooleanBinding undoableBinding = Bindings.createBooleanBinding(textInputControl::isUndoable, textInputControl.undoableProperty());
39+
BooleanBinding redoableBinding = Bindings.createBooleanBinding(textInputControl::isRedoable, textInputControl.redoableProperty());
3840

3941
this.executable.bind(
4042
switch (command) {
4143
case COPY -> editableBinding.and(maskTextBinding.not()).and(hasSelectionBinding);
4244
case CUT -> maskTextBinding.not().and(hasSelectionBinding);
4345
case PASTE -> editableBinding.and(hasStringInClipboardBinding);
4446
case DELETE -> editableBinding.and(hasSelectionBinding);
47+
case UNDO -> undoableBinding;
48+
case REDO -> redoableBinding;
4549
case SELECT_ALL -> {
4650
if (SHOW_HANDLES) {
4751
yield hasTextBinding.and(allSelectedBinding.not());
@@ -61,6 +65,8 @@ public void execute() {
6165
case PASTE -> textInputControl.paste();
6266
case DELETE -> textInputControl.deleteText(textInputControl.getSelection());
6367
case SELECT_ALL -> textInputControl.selectAll();
68+
case UNDO -> textInputControl.undo();
69+
case REDO -> textInputControl.redo();
6470
}
6571
textInputControl.requestFocus();
6672
}

src/main/java/org/jabref/gui/frame/JabRefFrame.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,10 @@ private void initKeyBindings() {
266266
event.consume();
267267
break;
268268
case SEARCH:
269-
globalSearchBar.focus();
269+
globalSearchBar.requestFocus();
270+
break;
271+
case OPEN_GLOBAL_SEARCH_DIALOG:
272+
globalSearchBar.openGlobalSearchDialog();
270273
break;
271274
case NEW_ARTICLE:
272275
new NewEntryAction(this::getCurrentLibraryTab, StandardEntryType.Article, dialogService, prefs, stateManager).execute();

src/main/java/org/jabref/gui/icon/IconTheme.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ public enum JabRefIcons implements JabRefIcon {
352352
SELECT_ICONS(MaterialDesignA.APPS),
353353
KEEP_SEARCH_STRING(MaterialDesignE.EARTH),
354354
KEEP_ON_TOP(MaterialDesignP.PIN),
355-
KEEP_ON_TOP_OFF(MaterialDesignP.PIN_OFF_OUTLINE),
355+
KEEP_ON_TOP_OFF(MaterialDesignP.PIN_OFF),
356356
OPEN_GLOBAL_SEARCH(MaterialDesignO.OPEN_IN_NEW),
357357
REMOVE_TAGS(MaterialDesignC.CLOSE),
358358
ACCEPT_LEFT(MaterialDesignS.SUBDIRECTORY_ARROW_LEFT),

src/main/java/org/jabref/gui/keyboard/KeyBinding.java

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public enum KeyBinding {
103103
SAVE_DATABASE("Save library", Localization.lang("Save library"), "ctrl+S", KeyBindingCategory.FILE),
104104
SAVE_DATABASE_AS("Save library as ...", Localization.lang("Save library as..."), "ctrl+shift+S", KeyBindingCategory.FILE),
105105
SEARCH("Search", Localization.lang("Search"), "ctrl+F", KeyBindingCategory.SEARCH),
106+
OPEN_GLOBAL_SEARCH_DIALOG("Open global search window", Localization.lang("Open global search window"), "ctrl+shift+F", KeyBindingCategory.SEARCH),
106107
SELECT_ALL("Select all", Localization.lang("Select all"), "ctrl+A", KeyBindingCategory.EDIT),
107108
SELECT_FIRST_ENTRY("Select first entry", Localization.lang("Select first entry"), "HOME", KeyBindingCategory.EDIT),
108109
SELECT_LAST_ENTRY("Select last entry", Localization.lang("Select last entry"), "END", KeyBindingCategory.EDIT),

src/main/java/org/jabref/gui/search/GlobalSearchBar.java

+43-56
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99

1010
import javax.swing.undo.UndoManager;
1111

12-
import javafx.application.Platform;
1312
import javafx.beans.binding.Bindings;
1413
import javafx.beans.binding.BooleanBinding;
1514
import javafx.beans.property.BooleanProperty;
1615
import javafx.beans.property.SimpleBooleanProperty;
1716
import javafx.collections.ListChangeListener;
18-
import javafx.collections.ObservableList;
17+
import javafx.collections.SetChangeListener;
1918
import javafx.css.PseudoClass;
2019
import javafx.event.Event;
2120
import javafx.geometry.Insets;
@@ -55,7 +54,6 @@
5554
import org.jabref.gui.keyboard.KeyBindingRepository;
5655
import org.jabref.gui.search.rules.describer.SearchDescribers;
5756
import org.jabref.gui.util.BindingsHelper;
58-
import org.jabref.gui.util.IconValidationDecorator;
5957
import org.jabref.gui.util.OptionalObjectProperty;
6058
import org.jabref.gui.util.TooltipTextUtil;
6159
import org.jabref.gui.util.UiTaskExecutor;
@@ -66,10 +64,6 @@
6664
import org.jabref.preferences.PreferencesService;
6765
import org.jabref.preferences.SearchPreferences;
6866

69-
import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator;
70-
import de.saxsys.mvvmfx.utils.validation.ValidationMessage;
71-
import de.saxsys.mvvmfx.utils.validation.Validator;
72-
import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer;
7367
import impl.org.controlsfx.skin.AutoCompletePopup;
7468
import org.controlsfx.control.textfield.AutoCompletionBinding;
7569
import org.controlsfx.control.textfield.CustomTextField;
@@ -99,7 +93,6 @@ public class GlobalSearchBar extends HBox {
9993

10094
private final StateManager stateManager;
10195
private final PreferencesService preferencesService;
102-
private final Validator regexValidator;
10396
private final UndoManager undoManager;
10497
private final LibraryTabContainer tabContainer;
10598

@@ -145,32 +138,22 @@ public GlobalSearchBar(LibraryTabContainer tabContainer,
145138

146139
searchField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
147140
if (keyBindingRepository.matches(event, KeyBinding.CLEAR_SEARCH)) {
148-
// Clear search and select first entry, if available
149141
searchField.clear();
150142
if (searchType == SearchType.NORMAL_SEARCH) {
151-
tabContainer.getCurrentLibraryTab().getMainTable().getSelectionModel().selectFirst();
143+
tabContainer.getCurrentLibraryTab().getMainTable().requestFocus();
152144
}
153145
event.consume();
154146
}
155147
});
156148

157-
searchField.setContextMenu(SearchFieldRightClickMenu.create(
158-
stateManager,
159-
searchField,
160-
tabContainer,
161-
undoManager));
162-
163-
ObservableList<String> search = stateManager.getWholeSearchHistory();
164-
search.addListener((ListChangeListener.Change<? extends String> change) ->
165-
searchField.setContextMenu(SearchFieldRightClickMenu.create(
166-
stateManager,
167-
searchField,
168-
tabContainer,
169-
undoManager))
170-
);
171-
172149
ClipBoardManager.addX11Support(searchField);
173150

151+
searchField.setContextMenu(SearchFieldRightClickMenu.create(stateManager, searchField));
152+
stateManager.getWholeSearchHistory().addListener((ListChangeListener.Change<? extends String> change) -> {
153+
searchField.getContextMenu().getItems().removeLast();
154+
searchField.getContextMenu().getItems().add(SearchFieldRightClickMenu.createSearchFromHistorySubMenu(stateManager, searchField));
155+
});
156+
174157
regularExpressionButton = IconTheme.JabRefIcons.REG_EX.asToggleButton();
175158
caseSensitiveButton = IconTheme.JabRefIcons.CASE_SENSITIVE.asToggleButton();
176159
fulltextButton = IconTheme.JabRefIcons.FULLTEXT.asToggleButton();
@@ -184,8 +167,7 @@ public GlobalSearchBar(LibraryTabContainer tabContainer,
184167
.or(caseSensitiveButton.focusedProperty())
185168
.or(fulltextButton.focusedProperty())
186169
.or(keepSearchString.focusedProperty())
187-
.or(searchField.textProperty()
188-
.isNotEmpty());
170+
.or(searchField.textProperty().isNotEmpty());
189171

190172
regularExpressionButton.visibleProperty().unbind();
191173
regularExpressionButton.visibleProperty().bind(focusedOrActive);
@@ -205,18 +187,10 @@ public GlobalSearchBar(LibraryTabContainer tabContainer,
205187

206188
modifierButtons.setAlignment(Pos.CENTER);
207189
searchField.setRight(new HBox(searchField.getRight(), modifierButtons));
208-
searchField.getStyleClass().add("search-field");
190+
searchField.getStyleClass().add("global-search-bar");
209191
searchField.setMinWidth(100);
210192
HBox.setHgrow(searchField, Priority.ALWAYS);
211193

212-
regexValidator = new FunctionBasedValidator<>(
213-
searchField.textProperty(),
214-
query -> !(regularExpressionButton.isSelected() && !validRegex()),
215-
ValidationMessage.error(Localization.lang("Invalid regular expression")));
216-
ControlsFxVisualizer visualizer = new ControlsFxVisualizer();
217-
visualizer.setDecoration(new IconValidationDecorator(Pos.CENTER_LEFT));
218-
Platform.runLater(() -> visualizer.initVisualization(regexValidator.getValidationStatus(), searchField));
219-
220194
if (searchType == SearchType.NORMAL_SEARCH) {
221195
this.getChildren().addAll(searchField, openGlobalSearchButton, currentResults);
222196
} else {
@@ -261,52 +235,64 @@ private void initSearchModifierButtons() {
261235
regularExpressionButton.setSelected(searchPreferences.isRegularExpression());
262236
regularExpressionButton.setTooltip(new Tooltip(Localization.lang("regular expression")));
263237
initSearchModifierButton(regularExpressionButton);
264-
regularExpressionButton.setOnAction(event -> {
265-
searchPreferences.setSearchFlag(SearchRules.SearchFlags.REGULAR_EXPRESSION, regularExpressionButton.isSelected());
238+
regularExpressionButton.selectedProperty().addListener((obs, oldVal, newVal) -> {
239+
searchPreferences.setSearchFlag(SearchRules.SearchFlags.REGULAR_EXPRESSION, newVal);
266240
updateSearchQuery();
267241
});
268242

269243
caseSensitiveButton.setSelected(searchPreferences.isCaseSensitive());
270244
caseSensitiveButton.setTooltip(new Tooltip(Localization.lang("Case sensitive")));
271245
initSearchModifierButton(caseSensitiveButton);
272-
caseSensitiveButton.setOnAction(event -> {
273-
searchPreferences.setSearchFlag(SearchRules.SearchFlags.CASE_SENSITIVE, caseSensitiveButton.isSelected());
246+
caseSensitiveButton.selectedProperty().addListener((obs, oldVal, newVal) -> {
247+
searchPreferences.setSearchFlag(SearchRules.SearchFlags.CASE_SENSITIVE, newVal);
274248
updateSearchQuery();
275249
});
276250

277251
fulltextButton.setSelected(searchPreferences.isFulltext());
278252
fulltextButton.setTooltip(new Tooltip(Localization.lang("Fulltext search")));
279253
initSearchModifierButton(fulltextButton);
280-
fulltextButton.setOnAction(event -> {
281-
searchPreferences.setSearchFlag(SearchRules.SearchFlags.FULLTEXT, fulltextButton.isSelected());
254+
fulltextButton.selectedProperty().addListener((obs, oldVal, newVal) -> {
255+
searchPreferences.setSearchFlag(SearchRules.SearchFlags.FULLTEXT, newVal);
282256
updateSearchQuery();
283257
});
284258

285259
keepSearchString.setSelected(searchPreferences.shouldKeepSearchString());
286260
keepSearchString.setTooltip(new Tooltip(Localization.lang("Keep search string across libraries")));
287261
initSearchModifierButton(keepSearchString);
288-
keepSearchString.setOnAction(evt -> {
289-
searchPreferences.setSearchFlag(SearchRules.SearchFlags.KEEP_SEARCH_STRING, keepSearchString.isSelected());
262+
keepSearchString.selectedProperty().addListener((obs, oldVal, newVal) -> {
263+
searchPreferences.setSearchFlag(SearchRules.SearchFlags.KEEP_SEARCH_STRING, newVal);
290264
updateSearchQuery();
291265
});
292266

293267
openGlobalSearchButton.disableProperty().bindBidirectional(globalSearchActive);
294268
openGlobalSearchButton.setTooltip(new Tooltip(Localization.lang("Search across libraries in a new window")));
295269
initSearchModifierButton(openGlobalSearchButton);
296-
openGlobalSearchButton.setOnAction(evt -> {
297-
globalSearchActive.setValue(true);
298-
if (globalSearchResultDialog == null) {
299-
globalSearchResultDialog = new GlobalSearchResultDialog(undoManager, tabContainer);
300-
}
301-
stateManager.activeGlobalSearchQueryProperty().setValue(searchQueryProperty.get());
302-
updateSearchQuery();
303-
dialogService.showCustomDialogAndWait(globalSearchResultDialog);
304-
globalSearchActive.setValue(false);
270+
openGlobalSearchButton.setOnAction(evt -> openGlobalSearchDialog());
271+
272+
searchPreferences.getObservableSearchFlags().addListener((SetChangeListener.Change<? extends SearchRules.SearchFlags> change) -> {
273+
regularExpressionButton.setSelected(searchPreferences.isRegularExpression());
274+
caseSensitiveButton.setSelected(searchPreferences.isCaseSensitive());
275+
fulltextButton.setSelected(searchPreferences.isFulltext());
276+
keepSearchString.setSelected(searchPreferences.shouldKeepSearchString());
305277
});
306278
}
307279

280+
public void openGlobalSearchDialog() {
281+
if (globalSearchActive.get()) {
282+
return;
283+
}
284+
globalSearchActive.setValue(true);
285+
if (globalSearchResultDialog == null) {
286+
globalSearchResultDialog = new GlobalSearchResultDialog(undoManager, tabContainer);
287+
}
288+
stateManager.activeGlobalSearchQueryProperty().setValue(searchQueryProperty.get());
289+
updateSearchQuery();
290+
dialogService.showCustomDialogAndWait(globalSearchResultDialog);
291+
globalSearchActive.setValue(false);
292+
}
293+
308294
private void initSearchModifierButton(ButtonBase searchButton) {
309-
searchButton.setCursor(Cursor.DEFAULT);
295+
searchButton.setCursor(Cursor.HAND);
310296
searchButton.setMinHeight(28);
311297
searchButton.setMaxHeight(28);
312298
searchButton.setMinWidth(28);
@@ -319,7 +305,8 @@ private void initSearchModifierButton(ButtonBase searchButton) {
319305
/**
320306
* Focuses the search field if it is not focused.
321307
*/
322-
public void focus() {
308+
@Override
309+
public void requestFocus() {
323310
if (!searchField.isFocused()) {
324311
searchField.requestFocus();
325312
}
@@ -339,7 +326,7 @@ public void updateSearchQuery() {
339326
}
340327

341328
// Invalid regular expression
342-
if (!regexValidator.getValidationStatus().isValid()) {
329+
if (regularExpressionButton.isSelected() && !validRegex()) {
343330
currentResults.setText(Localization.lang("Invalid regular expression"));
344331
return;
345332
}

src/main/java/org/jabref/gui/search/GlobalSearchResultDialog.fxml

-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
<?import javafx.scene.control.ToggleButton?>
77
<?import javafx.scene.control.Tooltip?>
88
<?import javafx.scene.layout.VBox?>
9-
<?import org.jabref.gui.icon.JabRefIconView?>
109
<?import javafx.scene.layout.HBox?>
1110
<DialogPane xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
1211
fx:controller="org.jabref.gui.search.GlobalSearchResultDialog"
@@ -15,9 +14,6 @@
1514
<VBox>
1615
<HBox fx:id="searchBarContainer" spacing="10" alignment="CENTER_RIGHT">
1716
<ToggleButton fx:id="keepOnTop" styleClass="icon-button,narrow" prefHeight="20.0" prefWidth="20.0">
18-
<graphic>
19-
<JabRefIconView glyph="KEEP_ON_TOP"/>
20-
</graphic>
2117
<tooltip>
2218
<Tooltip text="%Keep dialog always on top"/>
2319
</tooltip>

0 commit comments

Comments
 (0)