Skip to content

Commit a80435f

Browse files
BasilDimopoulosNicholasBiancalixtus
authored
Adding icon picker for group dialog issue#6142 (#7776)
* Add a button for icon picker in GroupDialog.fxml, and leave openIconPicker functions in GroupDialogView.java and GroupDialogViewModel.java for further backend use. * Add a modal dialog box when clicking the icon picker button, the controller is coding in GroupDialogView.java, openIconPicker(). * Modified the structure of the icon picker modal box to make it scroll. * Use TabPane to restructure the icon picker modal box, show all icon buttons * Finished the icon picker function and waiting to be tested. * Update CHANGELOG.md * Fixed imports, applied intellij suggestions * Integrated controlsfx GridView * WIP * Fixed old workaround * Added searchbox and converted stage to popover * Added borders to icons, fixed changelog and l10n * Removed corner radius for consistency * Load ikons directly from the IkonProvider Co-authored-by: siedlerchr [email protected] Co-authored-by: NicholasBian <[email protected]> Co-authored-by: Carl Christian Snethlage <[email protected]>
1 parent 2f36a33 commit a80435f

File tree

4 files changed

+101
-12
lines changed

4 files changed

+101
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve
1414
- We added the option to copy the DOI of an entry directly from the context menu copy submenu. [#7826](https://github.com/JabRef/jabref/issues/7826)
1515
- We added a fulltext search feature. [#2838](https://github.com/JabRef/jabref/pull/2838)
1616
- We added unprotect_terms to the list of bracketed pattern modifiers [#7826](https://github.com/JabRef/jabref/pull/7960)
17+
- We added an icon picker in group edit dialog. [#6142](https://github.com/JabRef/jabref/issues/6142)
1718

1819
### Changed
1920

src/main/java/org/jabref/gui/groups/GroupDialog.fxml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,14 @@
3939
<HBox spacing="10.0">
4040
<VBox HBox.hgrow="ALWAYS">
4141
<Label text="%Icon"/>
42-
<TextField fx:id="iconField"/>
42+
<HBox>
43+
<TextField fx:id="iconField"/>
44+
<Button fx:id="iconPickerButton" onAction="#openIconPicker" styleClass="icon-button,narrow">
45+
<graphic>
46+
<JabRefIconView glyph="SELECT_ICONS"/>
47+
</graphic>
48+
</Button>
49+
</HBox>
4350
</VBox>
4451
<VBox layoutX="10.0" layoutY="10.0" minWidth="130.0" maxWidth="130.0" prefWidth="130.0">
4552
<Label text="%Color"/>

src/main/java/org/jabref/gui/groups/GroupDialogView.java

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,33 @@
22

33
import java.util.EnumMap;
44
import java.util.EnumSet;
5+
import java.util.ServiceLoader;
56

67
import javafx.application.Platform;
8+
import javafx.collections.FXCollections;
9+
import javafx.collections.ObservableList;
10+
import javafx.collections.transformation.FilteredList;
711
import javafx.event.ActionEvent;
812
import javafx.fxml.FXML;
13+
import javafx.geometry.Insets;
14+
import javafx.geometry.Pos;
915
import javafx.scene.control.Button;
1016
import javafx.scene.control.ButtonType;
1117
import javafx.scene.control.CheckBox;
1218
import javafx.scene.control.ColorPicker;
1319
import javafx.scene.control.ComboBox;
1420
import javafx.scene.control.RadioButton;
1521
import javafx.scene.control.TextField;
22+
import javafx.scene.layout.Border;
23+
import javafx.scene.layout.BorderStroke;
24+
import javafx.scene.layout.BorderStrokeStyle;
25+
import javafx.scene.layout.CornerRadii;
26+
import javafx.scene.layout.VBox;
27+
import javafx.scene.paint.Color;
1628

1729
import org.jabref.gui.DialogService;
30+
import org.jabref.gui.icon.IconTheme;
31+
import org.jabref.gui.icon.JabrefIconProvider;
1832
import org.jabref.gui.util.BaseDialog;
1933
import org.jabref.gui.util.IconValidationDecorator;
2034
import org.jabref.gui.util.ViewModelListCellFactory;
@@ -28,13 +42,21 @@
2842

2943
import com.airhacks.afterburner.views.ViewLoader;
3044
import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer;
45+
import org.controlsfx.control.GridCell;
46+
import org.controlsfx.control.GridView;
47+
import org.controlsfx.control.PopOver;
48+
import org.controlsfx.control.textfield.CustomTextField;
49+
import org.kordamp.ikonli.Ikon;
50+
import org.kordamp.ikonli.IkonProvider;
51+
import org.kordamp.ikonli.javafx.FontIcon;
3152

3253
public class GroupDialogView extends BaseDialog<AbstractGroup> {
3354

3455
// Basic Settings
3556
@FXML private TextField nameField;
3657
@FXML private TextField descriptionField;
3758
@FXML private TextField iconField;
59+
@FXML private Button iconPickerButton;
3860
@FXML private ColorPicker colorField;
3961
@FXML private ComboBox<GroupHierarchyType> hierarchicalContextCombo;
4062

@@ -70,7 +92,11 @@ public class GroupDialogView extends BaseDialog<AbstractGroup> {
7092
private final ControlsFxVisualizer validationVisualizer = new ControlsFxVisualizer();
7193
private final GroupDialogViewModel viewModel;
7294

73-
public GroupDialogView(DialogService dialogService, BibDatabaseContext currentDatabase, PreferencesService preferencesService, AbstractGroup editedGroup, GroupDialogHeader groupDialogHeader) {
95+
public GroupDialogView(DialogService dialogService,
96+
BibDatabaseContext currentDatabase,
97+
PreferencesService preferencesService,
98+
AbstractGroup editedGroup,
99+
GroupDialogHeader groupDialogHeader) {
74100
viewModel = new GroupDialogViewModel(dialogService, currentDatabase, preferencesService, editedGroup, groupDialogHeader);
75101

76102
ViewLoader.view(this)
@@ -91,6 +117,7 @@ public GroupDialogView(DialogService dialogService, BibDatabaseContext currentDa
91117
getDialogPane().getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL);
92118

93119
final Button confirmDialogButton = (Button) getDialogPane().lookupButton(ButtonType.OK);
120+
confirmDialogButton.disableProperty().bind(viewModel.validationStatus().validProperty().not());
94121
// handle validation before closing dialog and calling resultConverter
95122
confirmDialogButton.addEventFilter(ActionEvent.ACTION, viewModel::validationHandler);
96123
}
@@ -168,15 +195,6 @@ public void initialize() {
168195
validationVisualizer.initVisualization(viewModel.texGroupFilePathValidatonStatus(), texGroupFilePath);
169196
nameField.requestFocus();
170197
});
171-
172-
// Binding to the button throws a NPE, since it doesn't exist yet. Working around.
173-
viewModel.validationStatus().validProperty().addListener((obs, _oldValue, validationStatus) -> {
174-
if (validationStatus) {
175-
getDialogPane().lookupButton(ButtonType.OK).setDisable(false);
176-
} else {
177-
getDialogPane().lookupButton(ButtonType.OK).setDisable(true);
178-
}
179-
});
180198
}
181199

182200
@FXML
@@ -188,4 +206,66 @@ private void texGroupBrowse() {
188206
private void openHelp() {
189207
viewModel.openHelpPage();
190208
}
209+
210+
@FXML
211+
private void openIconPicker() {
212+
ObservableList<Ikon> ikonList = FXCollections.observableArrayList();
213+
FilteredList<Ikon> filteredList = new FilteredList<>(ikonList);
214+
215+
for (IkonProvider provider : ServiceLoader.load(IkonProvider.class.getModule().getLayer(), IkonProvider.class)) {
216+
if (provider.getClass() != JabrefIconProvider.class) {
217+
ikonList.addAll(EnumSet.allOf(provider.getIkon()));
218+
}
219+
}
220+
221+
CustomTextField searchBox = new CustomTextField();
222+
searchBox.setPromptText(Localization.lang("Search") + "...");
223+
searchBox.setLeft(IconTheme.JabRefIcons.SEARCH.getGraphicNode());
224+
searchBox.textProperty().addListener((obs, oldValue, newValue) ->
225+
filteredList.setPredicate(ikon -> newValue.isEmpty() || ikon.getDescription().toLowerCase()
226+
.contains(newValue.toLowerCase())));
227+
228+
GridView<Ikon> ikonGridView = new GridView<>(FXCollections.observableArrayList());
229+
ikonGridView.setCellFactory(gridView -> new IkonliCell());
230+
ikonGridView.setPrefWidth(520);
231+
ikonGridView.setPrefHeight(400);
232+
ikonGridView.setHorizontalCellSpacing(4);
233+
ikonGridView.setVerticalCellSpacing(4);
234+
ikonGridView.setItems(filteredList);
235+
236+
VBox vBox = new VBox(10, searchBox, ikonGridView);
237+
vBox.setPadding(new Insets(10));
238+
239+
PopOver popOver = new PopOver(vBox);
240+
popOver.setDetachable(false);
241+
popOver.setArrowSize(0);
242+
popOver.setCornerRadius(0);
243+
popOver.setTitle("Icon picker");
244+
popOver.show(iconPickerButton);
245+
}
246+
247+
public class IkonliCell extends GridCell<Ikon> {
248+
@Override
249+
protected void updateItem(Ikon ikon, boolean empty) {
250+
super.updateItem(ikon, empty);
251+
if (empty || ikon == null) {
252+
setText(null);
253+
setGraphic(null);
254+
} else {
255+
FontIcon fontIcon = FontIcon.of(ikon);
256+
fontIcon.getStyleClass().setAll("font-icon");
257+
fontIcon.setIconSize(22);
258+
setGraphic(fontIcon);
259+
setAlignment(Pos.BASELINE_CENTER);
260+
setPadding(new Insets(1));
261+
setBorder(new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderStroke.THIN)));
262+
263+
setOnMouseClicked(event -> {
264+
iconField.textProperty().setValue(String.valueOf(fontIcon.getIconCode()));
265+
PopOver stage = (PopOver) this.getGridView().getParent().getScene().getWindow();
266+
stage.hide();
267+
});
268+
}
269+
}
270+
}
191271
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,8 @@ public enum JabRefIcons implements JabRefIcon {
333333
HOME(MaterialDesignH.HOME),
334334
LINK(MaterialDesignL.LINK),
335335
LINK_VARIANT(MaterialDesignL.LINK_VARIANT),
336-
PROTECT_STRING(MaterialDesignC.CODE_BRACES);
336+
PROTECT_STRING(MaterialDesignC.CODE_BRACES),
337+
SELECT_ICONS(MaterialDesignA.APPS);
337338

338339
private final JabRefIcon icon;
339340

0 commit comments

Comments
 (0)