Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replaced abandoned JFoenix controls #10046

Merged
merged 5 commits into from
Jul 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve
- We moved the custom entry types dialog into the preferences dialog. [#9760](https://github.com/JabRef/jabref/pull/9760)
- We moved the manage content selectors dialog to the library properties. [#9768](https://github.com/JabRef/jabref/pull/9768)
- We moved the preferences menu command from the options menu to the file menu. [#9768](https://github.com/JabRef/jabref/pull/9768)
- We reworked the cross ref labels in the entry editor and added a right click menu. [#10046](https://github.com/JabRef/jabref/pull/10046)
- We reorganized the order of tabs and settings in the library properties. [#9836](https://github.com/JabRef/jabref/pull/9836)
- We changed the handling of an "overflow" of authors at `[authIniN]`: JabRef uses `+` to indicate an overflow. Example: `[authIni2]` produces `A+` (instead of `AB`) for `Aachen and Berlin and Chemnitz`. [#9703](https://github.com/JabRef/jabref/pull/9703)
- We moved the preferences option to open the last edited files on startup to the 'General' tab. [#9808](https://github.com/JabRef/jabref/pull/9808)
Expand Down
19 changes: 9 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ repositories {
maven { url 'https://oss.sonatype.org/content/groups/public' }
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
maven { url 'https://jitpack.io' }
maven { url 'https://sandec.jfrog.io/artifactory/repo' }
}

configurations {
Expand Down Expand Up @@ -176,7 +177,11 @@ dependencies {
implementation 'com.tobiasdiez:easybind:2.2.1-SNAPSHOT'
implementation 'org.fxmisc.flowless:flowless:0.7.0'
implementation 'org.fxmisc.richtext:richtextfx:0.11.0'
implementation 'com.jfoenix:jfoenix:9.0.10'
implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '1.74.0') {
exclude module: 'javax.inject' // Split package, use only jakarta.inject
exclude group: 'org.apache.logging.log4j'
}

implementation 'org.controlsfx:controlsfx:11.1.2'

implementation 'org.jsoup:jsoup:1.16.1'
Expand Down Expand Up @@ -343,8 +348,6 @@ run {
'javafx.controls/com.sun.javafx.scene.control' : 'org.jabref',
'org.controlsfx.controls/impl.org.controlsfx.skin' : 'org.jabref',

'javafx.controls/com.sun.javafx.scene.control.behavior' : 'com.jfoenix',

// Not sure why we need to restate the controlfx exports
// Taken from here: https://github.com/controlsfx/controlsfx/blob/9.0.0/build.gradle#L1
'javafx.graphics/com.sun.javafx.scene' : 'org.controlsfx.controls',
Expand All @@ -365,12 +368,7 @@ run {
'javafx.controls/com.sun.javafx.scene.control' : 'org.jabref',

'javafx.controls/javafx.scene.control.skin' : 'org.controlsfx.controls',
'javafx.graphics/javafx.scene' : 'org.controlsfx.controls',

'javafx.controls/com.sun.javafx.scene.control.behavior' : 'com.jfoenix',
'javafx.base/com.sun.javafx.binding' : 'com.jfoenix',
'javafx.graphics/com.sun.javafx.stage' : 'com.jfoenix',
'javafx.base/com.sun.javafx.event' : 'com.jfoenix'
'javafx.graphics/javafx.scene' : 'org.controlsfx.controls'
]
}
}
Expand Down Expand Up @@ -588,7 +586,8 @@ jlink {
'org.mariadb.jdbc.credential.env.EnvCredentialPlugin',
'org.mariadb.jdbc.credential.system.PropertiesCredentialPlugin'
provides 'java.security.Provider' with 'org.bouncycastle.jce.provider.BouncyCastleProvider',
'org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider' }
'org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider'
}

jpackage {
outputDir = "distribution"
Expand Down
1 change: 0 additions & 1 deletion docs/code-howtos/javafx.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ JabRef makes heavy use of Properties and Bindings. These are wrappers around Obs
* [Validation framework](https://github.com/sialcasa/mvvmFX/wiki/Validation)
* [mvvm framework](https://github.com/sialcasa/mvvmFX/wiki)
* [CSS Reference](http://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html)
* [JFoenix](https://github.com/jfoenixadmin/JFoenix) Material Designs look & feel
* [JavaFX Documentation project](https://fxdocs.github.io/docs/html5/index.html): Collected information on JavaFX in a central place
* [FXExperience](http://fxexperience.com) JavaFX Links of the week
* [Foojay](https://foojay.io) Java and JavaFX tutorials
Expand Down
3 changes: 1 addition & 2 deletions eclipse.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ eclipse {
}

def javafxcontrols = entries.find { isJavafxControls(it) };
javafxcontrols.entryAttributes['add-exports'] = 'javafx.controls/com.sun.javafx.scene.control=org.jabref:javafx.controls/com.sun.javafx.scene.control.behavior=org.jabref:javafx.controls/javafx.scene.control=org.jabref:javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix';
javafxcontrols.entryAttributes['add-exports'] = 'javafx.controls/com.sun.javafx.scene.control=org.jabref:javafx.controls/com.sun.javafx.scene.control.behavior=org.jabref:javafx.controls/javafx.scene.control=org.jabref';
javafxcontrols.entryAttributes['add-opens'] = 'javafx.controls/com.sun.javafx.scene.control=org.jabref:javafx.controls/com.sun.javafx.scene.control.behavior=org.jabref:javafx.controls/javafx.scene.control=org.jabref:javafx.controls/javafx.scene.control.skin=org.controlsfx.controls';

def javafxgraphics = entries.find { isJavafxGraphics(it) };
javafxgraphics.entryAttributes['add-opens'] = 'javafx.graphics/javafx.scene=org.controlsfx.controls';
javafxgraphics.entryAttributes['add-exports'] = 'javafx.graphics/com.sun.javafx.stage=com.jfoenix';

def javafxbase = entries.find { isJavafxBase(it) };
javafxbase.entryAttributes['add-exports'] = 'javafx.base/com.sun.javafx.event=org.controlsfx.controls:';
Expand Down
8 changes: 0 additions & 8 deletions external-libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,6 @@ URL: https://github.com/lemire/javaewah
License: Apache-2.0
```

```yaml
Id: com.jfoenix:jfoenix
Project: JavaFX MAterial Design Library
URL: https://github.com/jfoenixadmin/JFoenix
License: Apache-2.0
```

```yaml
Id: com.konghq.unirest
Project: Unirest for Java
Expand Down Expand Up @@ -539,7 +532,6 @@ com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
com.googlecode.javaewah:JavaEWAH:1.1.13
com.h2database:h2-mvstore:2.1.214
com.jfoenix:jfoenix:9.0.10
com.konghq:unirest-java:3.14.1
com.microsoft.azure:applicationinsights-core:2.4.1
com.microsoft.azure:applicationinsights-logging-log4j2:2.4.1
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
// JavaFX
requires javafx.base;
requires javafx.graphics;
requires javafx.swing;
requires javafx.controls;
requires javafx.web;
requires javafx.fxml;
requires afterburner.fx;
requires com.jfoenix;
requires com.dlsc.gemsfx;
uses com.dlsc.gemsfx.TagsField;
requires de.saxsys.mvvmfx;

requires org.kordamp.ikonli.core;
Expand Down
29 changes: 25 additions & 4 deletions src/main/java/org/jabref/gui/Base.css
Original file line number Diff line number Diff line change
Expand Up @@ -1244,10 +1244,10 @@ We want to have a look that matches our icons in the tool-bar */
-fx-background-color: -jr-accent;
}

.jfx-color-picker:armed,
.jfx-color-picker:hover,
.jfx-color-picker:focused,
.jfx-color-picker {
.color-picker:armed,
.color-picker:hover,
.color-picker:focused,
.color-picker {
-fx-background-color: transparent, transparent, transparent, transparent;
-fx-background-radius: 0px;
-fx-background-insets: 0px;
Expand Down Expand Up @@ -1387,6 +1387,27 @@ We want to have a look that matches our icons in the tool-bar */
-fx-pref-height: 30px;
-fx-padding: 0px 0px 0px -8px;
-fx-margin: 0em;
-fx-border-style: none;
}

.tags-field {
-fx-pref-height: 30px;
-fx-margin: 0em;
-fx-border-style: none;
}

.tags-field > .flow-pane > .tag-view {
-fx-background-color: -fx-default-button;
-fx-text-fill: -fx-focused-text-base-color;
}

.tags-field > .flow-pane > .tag-view:selected {
-fx-background-color: derive(-fx-default-button, -20%);
-fx-text-fill: -fx-focused-text-base-color;
}

.tags-field-editor {
-fx-border-width: 0;
}

.searchBar:invalid {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
import java.io.IOException;
import java.util.Objects;

import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;

import org.jabref.architecture.AllowedToUseAwt;

Expand Down Expand Up @@ -51,7 +52,7 @@ public Image render(int width, int height) {
try {
int resolution = 96;
BufferedImage image = renderer.renderImageWithDPI(pageNumber, 2 * resolution, ImageType.RGB);
return SwingFXUtils.toFXImage(resize(image, width, height), null);
return convertToFxImage(resize(image, width, height));
} catch (IOException e) {
// TODO: LOG
return null;
Expand All @@ -68,4 +69,19 @@ public double getAspectRatio() {
PDRectangle mediaBox = page.getMediaBox();
return mediaBox.getWidth() / mediaBox.getHeight();
}

// See https://stackoverflow.com/a/57552025/3450689
private static Image convertToFxImage(BufferedImage image) {
WritableImage wr = null;
if (image != null) {
wr = new WritableImage(image.getWidth(), image.getHeight());
PixelWriter pw = wr.getPixelWriter();
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
pw.setArgb(x, y, image.getRGB(x, y));
}
}
}
return wr;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.HBox?>
<?import com.jfoenix.controls.JFXChipView?>
<?import com.dlsc.gemsfx.TagsField?>
<fx:root xmlns:fx="http://javafx.com/fxml/1" type="HBox" xmlns="http://javafx.com/javafx/8.0.112"
fx:controller="org.jabref.gui.fieldeditors.LinkedEntriesEditor">
<JFXChipView fx:id="chipView" HBox.hgrow="ALWAYS" maxWidth="Infinity"/>
<TagsField fx:id="entryLinkField" HBox.hgrow="ALWAYS"/>
</fx:root>
119 changes: 99 additions & 20 deletions src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java
Original file line number Diff line number Diff line change
@@ -1,53 +1,101 @@
package org.jabref.gui.fieldeditors;

import java.util.Comparator;
import java.util.stream.Collectors;

import javafx.beans.binding.Bindings;
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.input.MouseButton;
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.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.ParsedEntryLink;
import org.jabref.model.entry.field.Field;

import com.airhacks.afterburner.views.ViewLoader;
import com.jfoenix.controls.JFXChip;
import com.jfoenix.controls.JFXChipView;
import com.jfoenix.controls.JFXDefaultChip;
import com.dlsc.gemsfx.TagsField;
import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LinkedEntriesEditor extends HBox implements FieldEditorFX {

@FXML
private static final Logger LOGGER = LoggerFactory.getLogger(LinkedEntriesEditor.class);

@FXML public TagsField<ParsedEntryLink> entryLinkField;

@Inject private DialogService dialogService;
@Inject private ClipBoardManager clipBoardManager;
@Inject private KeyBindingRepository keyBindingRepository;

private final LinkedEntriesEditorViewModel viewModel;
@FXML
private JFXChipView<ParsedEntryLink> chipView;

public LinkedEntriesEditor(Field field, BibDatabaseContext databaseContext, SuggestionProvider<BibEntry> suggestionProvider, FieldCheckers fieldCheckers) {
this.viewModel = new LinkedEntriesEditorViewModel(field, suggestionProvider, databaseContext, fieldCheckers);

ViewLoader.view(this)
.root(this)
.load();

chipView.setConverter(viewModel.getStringConverter());
var autoCompletionItemFactory = new ViewModelListCellFactory<ParsedEntryLink>()
.withText(ParsedEntryLink::getKey);
chipView.getAutoCompletePopup().setSuggestionsCellFactory(autoCompletionItemFactory);
chipView.getAutoCompletePopup().setCellLimit(5);
chipView.getSuggestions().addAll(suggestionProvider.getPossibleSuggestions().stream().map(ParsedEntryLink::new).collect(Collectors.toList()));

chipView.setChipFactory((view, item) -> {
JFXChip<ParsedEntryLink> chip = new JFXDefaultChip<>(view, item);
chip.setOnMouseClicked(event -> viewModel.jumpToEntry(item));
return chip;
this.viewModel = new LinkedEntriesEditorViewModel(field, suggestionProvider, databaseContext, fieldCheckers);

entryLinkField.setCellFactory(new ViewModelListCellFactory<ParsedEntryLink>().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.setTagViewFactory(this::createTag);
entryLinkField.setConverter(viewModel.getStringConverter());
entryLinkField.setNewItemProducer(searchText -> viewModel.getStringConverter().fromString(searchText));
entryLinkField.setMatcher((entryLink, searchText) -> entryLink.getKey().toLowerCase().startsWith(searchText.toLowerCase()));
entryLinkField.setComparator(Comparator.comparing(ParsedEntryLink::getKey));
entryLinkField.setShowSearchIcon(false);
entryLinkField.getEditor().getStyleClass().clear();
entryLinkField.getEditor().getStyleClass().add("tags-field-editor");

Bindings.bindContentBidirectional(entryLinkField.getTags(), viewModel.linkedEntriesProperty());
}

private Node createTag(ParsedEntryLink entryLink) {
Label tagLabel = new Label();
tagLabel.setText(entryLinkField.getConverter().toString(entryLink));
tagLabel.setGraphic(IconTheme.JabRefIcons.REMOVE_TAGS.getGraphicNode());
tagLabel.getGraphic().setOnMouseClicked(event -> entryLinkField.removeTags(entryLink));
tagLabel.setContentDisplay(ContentDisplay.RIGHT);
tagLabel.setOnMouseClicked(event -> {
if (event.getClickCount() == 2 && event.getButton().equals(MouseButton.PRIMARY)) {
viewModel.jumpToEntry(entryLink);
}
});

Bindings.bindContentBidirectional(chipView.getChips(), viewModel.linkedEntriesProperty());
ContextMenu contextMenu = new ContextMenu();
ActionFactory factory = new ActionFactory(keyBindingRepository);
contextMenu.getItems().addAll(
factory.createMenuItem(StandardActions.COPY, new TagContextAction(StandardActions.COPY, entryLink)),
factory.createMenuItem(StandardActions.CUT, new TagContextAction(StandardActions.CUT, entryLink)),
factory.createMenuItem(StandardActions.DELETE, new TagContextAction(StandardActions.DELETE, entryLink))
);
tagLabel.setContextMenu(contextMenu);
return tagLabel;
}

public LinkedEntriesEditorViewModel getViewModel() {
Expand All @@ -63,4 +111,35 @@ public void bindToEntry(BibEntry entry) {
public Parent getNode() {
return this;
}

private class TagContextAction extends SimpleCommand {
private final StandardActions command;
private final ParsedEntryLink entryLink;

public TagContextAction(StandardActions command, ParsedEntryLink entryLink) {
this.command = command;
this.entryLink = entryLink;
}

@Override
public void execute() {
switch (command) {
case COPY -> {
clipBoardManager.setContent(entryLink.getKey());
dialogService.notify(Localization.lang("Copied '%0' to clipboard.",
JabRefDialogService.shortenDialogMessage(entryLink.getKey())));
}
case CUT -> {
clipBoardManager.setContent(entryLink.getKey());
dialogService.notify(Localization.lang("Copied '%0' to clipboard.",
JabRefDialogService.shortenDialogMessage(entryLink.getKey())));
entryLinkField.removeTags(entryLink);
}
case DELETE ->
entryLinkField.removeTags(entryLink);
default ->
LOGGER.info("Action {} not defined", command.getText());
}
}
}
}
4 changes: 1 addition & 3 deletions src/main/java/org/jabref/gui/icon/IconTheme.java
Original file line number Diff line number Diff line change
Expand Up @@ -345,11 +345,9 @@ public enum JabRefIcons implements JabRefIcon {
KEEP_ON_TOP(MaterialDesignP.PIN),
KEEP_ON_TOP_OFF(MaterialDesignP.PIN_OFF_OUTLINE),
OPEN_GLOBAL_SEARCH(MaterialDesignO.OPEN_IN_NEW),

REMOVE_TAGS(MaterialDesignC.CLOSE),
ACCEPT_LEFT(MaterialDesignS.SUBDIRECTORY_ARROW_LEFT),

ACCEPT_RIGHT(MaterialDesignS.SUBDIRECTORY_ARROW_RIGHT),

MERGE_GROUPS(MaterialDesignS.SOURCE_MERGE);

private final JabRefIcon icon;
Expand Down
Loading