From 31467beb777ce8cd7e5d9db7961f7a0a6b7878e1 Mon Sep 17 00:00:00 2001 From: Ziyao-Jin <141754585+Ziyao-Jin@users.noreply.github.com> Date: Sat, 23 Dec 2023 22:31:17 +0800 Subject: [PATCH] Add auto group colour assignment (#10521) * Add auto group colour assignment * Add clickable option for auto colour assignment * Change button to checkbox * Start working on using the context of the current group for coloring Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> * First short for colorful subgroups * When creating a new group, it inhertics the icon of the parent group * Add checkbox for coloring of groups Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> * Help button leaves dialog opened Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> * Fix missing "Collect by" header Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> --------- Co-authored-by: Oliver Kopp Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> --- CHANGELOG.md | 1 + build.gradle | 3 + src/main/java/module-info.java | 2 + .../jabref/gui/actions/StandardActions.java | 1 + .../jabref/gui/groups/GroupColorPicker.java | 62 ++++++++++++++ .../org/jabref/gui/groups/GroupDialog.fxml | 37 ++++----- .../jabref/gui/groups/GroupDialogView.java | 81 ++++++++++++++++--- .../gui/groups/GroupDialogViewModel.java | 48 ++++++++--- .../jabref/gui/groups/GroupTreeViewModel.java | 2 + .../logic/groups/DefaultGroupsFactory.java | 5 +- .../jabref/model/groups/AbstractGroup.java | 2 +- .../gui/groups/GroupColorPickerTest.java | 49 +++++++++++ .../gui/groups/GroupDialogViewModelTest.java | 6 +- 13 files changed, 258 insertions(+), 41 deletions(-) create mode 100644 src/main/java/org/jabref/gui/groups/GroupColorPicker.java create mode 100644 src/test/java/org/jabref/gui/groups/GroupColorPickerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 606bf9aa981..20899f15b4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added [scholar.archive.org](https://scholar.archive.org/) as a new fetcher. [#10498](https://github.com/JabRef/jabref/issues/10498) - We integrated predatory journal checking as part of the Integrity Checker based on the [check-bib-for-predatory](https://github.com/CfKu/check-bib-for-predatory). [koppor#348](https://github.com/koppor/jabref/issues/348) - We added a 'More options' section in the main table right click menu opening the preferences dialog. [#9432](https://github.com/JabRef/jabref/issues/9432) +- When creating a new group, it inherits the icon of the parent group. [#10521](https://github.com/JabRef/jabref/pull/10521) ### Changed diff --git a/build.gradle b/build.gradle index 5ca46c13169..868dd933e7f 100644 --- a/build.gradle +++ b/build.gradle @@ -235,6 +235,9 @@ dependencies { // Allow objects "magically" to be mapped to JSON using GSON // implementation 'org.glassfish.jersey.media:jersey-media-json-gson:3.1.1' + // Because of GraalVM quirks, we need to ship that. See https://github.com/jspecify/jspecify/issues/389#issuecomment-1661130973 for details + implementation 'org.jspecify:jspecify:0.3.0' + testImplementation 'io.github.classgraph:classgraph:4.8.165' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.10.1' diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 0ac40fe2aa4..48267ada078 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -136,6 +136,8 @@ uses org.eclipse.jgit.transport.SshSessionFactory; uses org.eclipse.jgit.lib.GpgSigner; + requires transitive org.jspecify; + // other libraries requires org.antlr.antlr4.runtime; requires org.libreoffice.uno; diff --git a/src/main/java/org/jabref/gui/actions/StandardActions.java b/src/main/java/org/jabref/gui/actions/StandardActions.java index b53ecee6cc0..ee4e324373b 100644 --- a/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -158,6 +158,7 @@ public enum StandardActions implements Action { DELETE_FILE(Localization.lang("Permanently delete local file"), IconTheme.JabRefIcons.DELETE_FILE, KeyBinding.DELETE_ENTRY), HELP(Localization.lang("Online help"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), + HELP_GROUPS(Localization.lang("Open Help page"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), HELP_KEY_PATTERNS(Localization.lang("Help on key patterns"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), HELP_REGEX_SEARCH(Localization.lang("Help on regular expression search"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), HELP_NAME_FORMATTER(Localization.lang("Help on Name Formatting"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), diff --git a/src/main/java/org/jabref/gui/groups/GroupColorPicker.java b/src/main/java/org/jabref/gui/groups/GroupColorPicker.java new file mode 100644 index 00000000000..78fcafe65cf --- /dev/null +++ b/src/main/java/org/jabref/gui/groups/GroupColorPicker.java @@ -0,0 +1,62 @@ +package org.jabref.gui.groups; + +import java.util.List; + +import javafx.scene.paint.Color; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +public class GroupColorPicker { + + // Generate color for top groups + public static Color generateColor(List siblingColors) { + return generateColor(siblingColors, null); + } + + /** + * Algorithm optimized for colors, not for gray-scale (where it does not work) + */ + public static Color generateColor(List siblingColors, @Nullable Color parentColor) { + if (siblingColors.isEmpty()) { + if (parentColor == null) { + // We need something colorful to derive other colors based on the color + return Color.hsb(Math.random() * 360.0, .50, .75); + } + return generateSubGroupColor(parentColor); + } + + double sumSin = 0; + double sumCos = 0; + + // Calculate the mean angle + for (Color color : siblingColors) { + double hue = color.getHue(); + sumSin += Math.sin(Math.toRadians(hue)); + sumCos += Math.cos(Math.toRadians(hue)); + } + + double meanAngle = Math.toDegrees(Math.atan2(sumSin, sumCos)); + meanAngle = (meanAngle + 360) % 360; + + // The opposite angle is potentially the point of maximum average distance + double newHue = (meanAngle + 180) % 360; + + double sumSaturation = 0; + double sumBrightness = 0; + for (Color color : siblingColors) { + sumSaturation += color.getSaturation(); + sumBrightness += color.getBrightness(); + } + + double averageSaturation = sumSaturation / siblingColors.size(); + double averageBrightness = sumBrightness / siblingColors.size(); + + return Color.hsb(newHue, averageSaturation, averageBrightness); + } + + private static Color generateSubGroupColor(Color baseColor) { + return baseColor.deriveColor(0.0, 1.0, .9, 1.0); + } +} diff --git a/src/main/java/org/jabref/gui/groups/GroupDialog.fxml b/src/main/java/org/jabref/gui/groups/GroupDialog.fxml index 3d12f80abbc..02b19876250 100644 --- a/src/main/java/org/jabref/gui/groups/GroupDialog.fxml +++ b/src/main/java/org/jabref/gui/groups/GroupDialog.fxml @@ -37,7 +37,7 @@ - + - - - + + + + + + + + + + + - -