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

[WIP] Convert Exporter Customization Dialog to javafx #4394

Merged
merged 78 commits into from
Dec 9, 2018

Conversation

abepolk
Copy link
Contributor

@abepolk abepolk commented Oct 21, 2018

This is my work so far. I am just beginning the dialog conversion and wanted to share my reasoning so far. What I am doing is listed in the comments in class ExportCustomizationDialogViewModel.


  • Change in CHANGELOG.md described
  • Tests created for changes
  • Manually tested changed features in running JabRef
  • Screenshots added in PR description (for bigger UI changes)
  • Ensured that the git commit message is a good one
  • Check documentation status (Issue created for outdated help page at help.jabref.org?)

@@ -35,4 +35,8 @@

public void updateEntryEditorTabList();

List<String> getStringList(String key);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please no generic methods here, create specific ones for the cases you need, e.g. get CustomExporters or getExporterormat... Or you create CustomExporter preferences which has all the needed information inside.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Swing version has a class CustomExportList which holds a list of custom exporters and associated data. I was going to move some of the code here into the ExporterViewModel and other code into the ExporterCustomizationDialogViewModel. Do you recommend keeping the CustomExportList as it is?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PreferencesService should only contain usecase-specific preference methods and the caller shouldn't care about how these information are stored in the preferences. So, here, the method would look like List<Exporter> getCustomExportFormats() and then in the implementation there would be calls like preferences.getStringList(JabRefPreferences.CUSTOM_EXPORT_FORMAT + i)).

Thus, most of the code in CustomExportList should go into JabrefPreferences.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the methods in preferencesService match those invoked in ExportCustomizationDialogViewModel as exporters.add(preferences.X) where X is the method, rather than using preferences.getStringList?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't really understand what you mean. The PreferencesService should provide an interface to the preferences that hide the exact semantics of how the object is serialized. So abstractly, X getX() and putX(X newValue), where X is the (model) object. The implementation in JabRefPreferences than has to worry about (de)serialization.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick question: Do you recommend writing tests for the view model? The dialog is simple, but not as simple as the previous one I wrote.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of writing tests for the sake of it. My strategy is usually to program everything and when I discover a problem in the logic during testing, I'll write a test for it. Hence, if the dialog is simple enough that you don't make any errors, then you don't need to write tests ;-).
An exception is when there is really complex logic that is definitely worth testing.

Copy link
Member

@tobiasdiez tobiasdiez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of your comments in the code make sense to me. I've added some further remarks.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExportCustomizationDialogViewModel extends BaseDialog<Void> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The View/Dialog class should derive from BaseDialog and not the view model.

@@ -35,4 +35,8 @@

public void updateEntryEditorTabList();

List<String> getStringList(String key);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PreferencesService should only contain usecase-specific preference methods and the caller shouldn't care about how these information are stored in the preferences. So, here, the method would look like List<Exporter> getCustomExportFormats() and then in the implementation there would be calls like preferences.getStringList(JabRefPreferences.CUSTOM_EXPORT_FORMAT + i)).

Thus, most of the code in CustomExportList should go into JabrefPreferences.

}

//possibly not necessary, and if so getSortedList will have to return the correct type, not Eventlist<List<String>>
public List<ExporterViewModel> loadExporters() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might don't need the gui-wrapper ExporterViewModel around Exporter. In general such a wrapper is needed to adapt the properties of the model to display it to the user. But, here, the data of an exporter is relatively simple and the already in a suitable format for the gui.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have put the data in the GUI wrapper ExporterViewModel for now. How would I create the TableView and TableColumns without a ViewModel for the data? All the examples I could find in JabRef and online had some sort of ViewModel.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also simply use Exporter as the datatype for the TableView. But if you have already created a ViewModel for it, keep it. It is probably the cleaner approach and more versatile for the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. I've finished, but not tested, most of the ViewModel. I just have to add the dialog to add or modify the exporter, and then create the View and FXML. Unless there's something I forgot...

@abepolk
Copy link
Contributor Author

abepolk commented Oct 28, 2018

At this point, I've refactored most of the code from the dialogs. The reasoning is mostly implemented already or in the comments. Take a look if you have time and let me know what you think! Otherwise, I can keep going and let you know when I'm done.

.addExtensionFilter(Localization.lang("Custom layout file"), StandardFileType.LAYOUT)
.withDefaultExtension(Localization.lang("Custom layout file"), StandardFileType.LAYOUT)
.withInitialDirectory(getExportWorkingDirectory()).build();
dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(f -> setText(f.toAbsolutePath().toString())); //implement setting the text
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To set the text, you would create a StringProperty here, e.g. selectedPath and then bind that property to a textfield in the View

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed it. I think I had the property, but I hadn't realized this is what I needed here. Thanks for your recommendation!


}

public void modifyExporter(int row) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really need an int here? It would be generally better if you just operate on the row object

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I need it. I changed it to use a property selectedExporters.

@Siedlerchr Siedlerchr mentioned this pull request Nov 1, 2018
6 tasks
@abepolk
Copy link
Contributor Author

abepolk commented Nov 2, 2018

Hi, I am having trouble using EasyBind to bind a ListProperty in the view model to a table view in the View. Specifically, I want to bind viewModel.selectedExportersProperty to the selected rows in exporterTable within class ExportCustomizationDialogView. I am using KeyBindingsDialogView as an example of how to use EasyBind. However, KeyBindingsDialogView uses a treeTable structure in lines 56-59, which is not what I want in my dialog. I have spent at least four hours today reading about JavaFX, data binding, properties, observers, and other things, but I still can't figure it out. I have some ideas on the recent pull request, but Eclipse still throws compiler errors. Any ideas on what I should do?

@Siedlerchr
Copy link
Member

@NorwayMaple No need to complex binding. Just use setItems.
I created a simple patch fixing some of the issues you mentioned and generated some stubs.
Remove the .txt extensions and import it with git am < file.patch

Another thing I just noticed, for the SimpleXYZProperty in javafx, there exists always an interface, e.g. XYZ Property. Always use that.

0001-fix-some-binding-issues.patch.txt

@abepolk
Copy link
Contributor Author

abepolk commented Nov 2, 2018

Thank you! I took a brief look at it, and it looks good. In the future, how can I decide when to use EasyBind?

@Siedlerchr
Copy link
Member

@NorwayMaple Easybind is only needed in rare cases. I would go with the rule of thumb, if you can't achieve your goal with normal javafx bindings, try Easbindings.

@abepolk
Copy link
Contributor Author

abepolk commented Nov 3, 2018

I finished the View and am trying to load it from the JabRef menu, but Afterburner throws an error:

14:44:21.187 [JavaFX Application Thread] ERROR org.jabref.FallbackExceptionHandler - Uncaught exception occurred in Thread[JavaFX Application Thread,5,main] - java.lang.IllegalStateException: Cannot load org.jabref.gui.exporter.exportcustomizationdialog
	at com.airhacks.afterburner.views.ViewLoader.load(ViewLoader.java:174)

etc...

I grepped exportcustomizationdialog and ExportCustomizationDialog, which is the old Swing version, in src/, and all references to the old Swing dialog are in the Swing dialog itself. Any ideas what is going on?

@abepolk
Copy link
Contributor Author

abepolk commented Nov 27, 2018

I made a number of the changes. For some reason, the cancel button is not showing up in the subdialog. Also, I tried to use a try/catch block to catch an IndexOutOfBoundsException when selectedExporters.get(0) is called on an empty list, i.e. no exporters are selected. However the exception doesn't get caught. Separately (but not pushed), I tried just doing a check for it if (selectedExporters.isEmpty()) {return;}, but that led to the same uncaught exception.

try {
exporterToModify = selectedExporters.get(0);
dialog = new CreateModifyExporterDialogView(exporterToModify, dialogService, preferences, loader);
} catch (IndexOutOfBoundsException ex) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normally isEmpty should work. Does the indexoutofbounds exception realy targets to this line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear. I put debug breakpoints on lines 56, 57, 59, and 60. I also put one in the line viewModel.modifyExporter() in ExporterCustomizationDialogView. That one is the only one it stops at before it throws the exception, but I don't understand how that is possible, since there should be no intervening lines. Also I find it odd that it should throw the very same exception that I was trying to catch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can try to make selectedExporters a normal array list.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it has to do with selectedExporters because it returns at line 59 when none are selected. Something else may be going on.

@abepolk
Copy link
Contributor Author

abepolk commented Dec 7, 2018

@Siedlerchr @tobiasdiez I think I have finished the corrections you guys recommended. Do you think anything remains to be changed?

Thanks,
Abe

Copy link
Member

@tobiasdiez tobiasdiez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the followup. The code looks very good to me. Please remove the outdated comments (I only marked a few, there might be more) and maybe run the code formatter on the files (also on the fxml ones), then this is good to go.


@Inject private DialogService dialogService;
@Inject private PreferencesService preferences;
@Inject private JournalAbbreviationLoader loader; // Should this be injected?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g here

}

private void loadExporters() {
List<TemplateExporter> exportersLogic = preferences.getCustomExportFormats(loader); //Var exportersLogic may need more descriptive name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or here

@abepolk
Copy link
Contributor Author

abepolk commented Dec 8, 2018

I removed most of the comments - let me know if I need to remove the last few. For the code formatter, I ran it, then git reset --hard and just removed some extra newlines (it wanted me to add indents that didn't make sense as well). Thanks for your help!

Copy link
Member

@tobiasdiez tobiasdiez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Thanks a lot for all the work you put in this PR. I will now press the magic button 😃

@tobiasdiez tobiasdiez merged commit 41f2d61 into JabRef:master Dec 9, 2018
Siedlerchr added a commit that referenced this pull request Dec 23, 2018
* upstream/master:
  Bump applicationinsights-logging-log4j2 from 2.2.1 to 2.3.0 (#4540)
  Bump antlr4-runtime from 4.7.1 to 4.7.2 (#4542)
  Bump applicationinsights-core from 2.2.1 to 2.3.0 (#4541)
  Bump antlr4 from 4.7.1 to 4.7.2 (#4543)
  Fix journal name "Astronomy Journal" to "Astronomical Journal"
  fix-for-issue-4489 (#4538)
  Sorting of read-status isn't working as expected #4521 (#4536)
  Add preselect last used export format in export to clipboard dialog (#4533)
  fixed 4365 put html in clipboard (#4519)
  [WIP] Convert Exporter Customization Dialog to javafx (#4394)
  Fixes #4437 (#4531)
  Bump checkstyle from 8.14 to 8.15 (#4528)

# Conflicts:
#	src/main/java/org/jabref/preferences/PreferencesService.java
Siedlerchr added a commit that referenced this pull request Dec 28, 2018
* upstream/master: (49 commits)
  improve styling of preferences side menu (#4556)
  Cleanup interfaces (#4553)
  Bump fontbox from 2.0.12 to 2.0.13 (#4552)
  Bump pdfbox from 2.0.12 to 2.0.13 (#4551)
  Bump wiremock from 2.19.0 to 2.20.0 (#4550)
  Fixes that renaming a group did not change the group name in the interface (#4549)
  Bump applicationinsights-logging-log4j2 from 2.2.1 to 2.3.0 (#4540)
  Bump antlr4-runtime from 4.7.1 to 4.7.2 (#4542)
  Bump applicationinsights-core from 2.2.1 to 2.3.0 (#4541)
  Bump antlr4 from 4.7.1 to 4.7.2 (#4543)
  Fix journal name "Astronomy Journal" to "Astronomical Journal"
  fix-for-issue-4489 (#4538)
  Sorting of read-status isn't working as expected #4521 (#4536)
  Add preselect last used export format in export to clipboard dialog (#4533)
  fixed 4365 put html in clipboard (#4519)
  [WIP] Convert Exporter Customization Dialog to javafx (#4394)
  Fixes #4437 (#4531)
  Bump checkstyle from 8.14 to 8.15 (#4528)
  Correct PNAS abbrev. (#4524)
  Create test (#4518)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants