diff --git a/src/main/java/io/github/dsheirer/gui/preference/application/AppearancePreferenceEditor.java b/src/main/java/io/github/dsheirer/gui/preference/application/AppearancePreferenceEditor.java index 5d2ddbe3..358120cd 100644 --- a/src/main/java/io/github/dsheirer/gui/preference/application/AppearancePreferenceEditor.java +++ b/src/main/java/io/github/dsheirer/gui/preference/application/AppearancePreferenceEditor.java @@ -25,6 +25,7 @@ import io.github.dsheirer.preference.application.ApplicationPreference; import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; import javafx.scene.control.Label; import javafx.scene.control.RadioButton; import javafx.scene.control.Separator; @@ -45,6 +46,7 @@ public class AppearancePreferenceEditor extends HBox private final ApplicationPreference mApplicationPreference; private GridPane mEditorPane; private VBox mThemeRadioGroup; + private CheckBox mComplementaryAliasColorsCheckBox; private Slider mGuiScaleSlider; private Label mGuiScaleValueLabel; @@ -74,6 +76,12 @@ public class AppearancePreferenceEditor extends HBox mEditorPane.add(new Label("Theme"), 0, row, 3, 1); mEditorPane.add(getThemeRadioGroup(), 0, ++row, 3, 1); + mEditorPane.add(getComplementaryAliasColorsCheckBox(), 0, ++row, 3, 1); + + Label aliasColorHint = new Label("Dark themes only. Replaces alias colors that are hard to read against " + + "the background with a complementary color."); + aliasColorHint.setWrapText(true); + mEditorPane.add(aliasColorHint, 0, ++row, 3, 1); Separator separator = new Separator(Orientation.HORIZONTAL); GridPane.setHgrow(separator, Priority.ALWAYS); @@ -139,6 +147,22 @@ public class AppearancePreferenceEditor extends HBox return mThemeRadioGroup; } + /** + * Check box to opt in to replacing hard to read alias colors with a complementary color in dark themes. + */ + private CheckBox getComplementaryAliasColorsCheckBox() + { + if(mComplementaryAliasColorsCheckBox == null) + { + mComplementaryAliasColorsCheckBox = new CheckBox("Use Complementary Alias Colors"); + mComplementaryAliasColorsCheckBox.setSelected(mApplicationPreference.isComplementaryAliasColors()); + mComplementaryAliasColorsCheckBox.setOnAction(e -> mApplicationPreference + .setComplementaryAliasColors(mComplementaryAliasColorsCheckBox.isSelected())); + } + + return mComplementaryAliasColorsCheckBox; + } + /** * Slider to adjust the global GUI zoom factor. The slider snaps to 10% increments and only * persists the value when the user releases the slider so we do not thrash a LAF reinstall on diff --git a/src/main/java/io/github/dsheirer/gui/theme/ThemeManager.java b/src/main/java/io/github/dsheirer/gui/theme/ThemeManager.java index f57afc3b..fdf8798e 100644 --- a/src/main/java/io/github/dsheirer/gui/theme/ThemeManager.java +++ b/src/main/java/io/github/dsheirer/gui/theme/ThemeManager.java @@ -74,6 +74,7 @@ public class ThemeManager private volatile UserPreferences mUserPreferences; private volatile Theme mCurrentTheme = Theme.LIGHT; private volatile double mCurrentGuiScale = 1.0d; + private volatile boolean mComplementaryAliasColors = false; /** Data URL of the currently-active per-theme accent stylesheet, or null. */ private volatile String mAccentStylesheetUrl; @@ -109,6 +110,7 @@ public class ThemeManager mUserPreferences = userPreferences; mCurrentTheme = userPreferences.getApplicationPreference().getTheme(); mCurrentGuiScale = userPreferences.getApplicationPreference().getGuiScale(); + mComplementaryAliasColors = userPreferences.getApplicationPreference().isComplementaryAliasColors(); if(firstBind) { @@ -225,12 +227,12 @@ public class ThemeManager * Provides the color to render user-selected content (e.g. an alias) against a background in the current theme. * @param color selected by the user. * @param background that the color is rendered against. - * @return the color, or a readable alternative when dark mode is active and the color lacks contrast with the - * background. + * @return the color, or a readable alternative when the user enabled complementary alias colors, dark mode is + * active and the color lacks contrast with the background. */ public Color getReadableColor(Color color, Color background) { - return isDarkMode() ? ColorContrast.getReadable(color, background) : color; + return mComplementaryAliasColors && isDarkMode() ? ColorContrast.getReadable(color, background) : color; } public void register(Scene scene) @@ -288,6 +290,20 @@ public class ThemeManager Theme updated = mUserPreferences.getApplicationPreference().getTheme(); double updatedScale = mUserPreferences.getApplicationPreference().getGuiScale(); + boolean updatedComplementary = mUserPreferences.getApplicationPreference().isComplementaryAliasColors(); + + if(updatedComplementary != mComplementaryAliasColors) + { + mComplementaryAliasColors = updatedComplementary; + + //The Swing table renderers look up the alias color each time they paint. + EventQueue.invokeLater(() -> { + for(Window window: Window.getWindows()) + { + window.repaint(); + } + }); + } if(updated == mCurrentTheme && Math.abs(updatedScale - mCurrentGuiScale) < 1e-6) { diff --git a/src/main/java/io/github/dsheirer/preference/application/ApplicationPreference.java b/src/main/java/io/github/dsheirer/preference/application/ApplicationPreference.java index 04133836..22b36257 100644 --- a/src/main/java/io/github/dsheirer/preference/application/ApplicationPreference.java +++ b/src/main/java/io/github/dsheirer/preference/application/ApplicationPreference.java @@ -37,6 +37,7 @@ public class ApplicationPreference extends Preference private static final String PREFERENCE_KEY_DARK_MODE = "dark.mode"; private static final String PREFERENCE_KEY_THEME = "ui.theme"; private static final String PREFERENCE_KEY_GUI_SCALE = "ui.gui.scale"; + private static final String PREFERENCE_KEY_COMPLEMENTARY_ALIAS_COLORS = "ui.complementary.alias.colors"; /** Hard bounds for the GUI zoom factor. */ public static final double MIN_GUI_SCALE = 0.5d; @@ -49,6 +50,7 @@ public class ApplicationPreference extends Preference private Boolean mAutomaticDiagnosticMonitoring; private Theme mTheme; private Double mGuiScale; + private Boolean mComplementaryAliasColors; /** * Constructs an instance @@ -167,6 +169,32 @@ public class ApplicationPreference extends Preference return getTheme().isDark(); } + /** + * Indicates if alias colors that lack contrast with the background are replaced with a complementary color when a + * dark theme is active. + * @return true if enabled. Default is false, so that aliases display the color selected by the user. + */ + public boolean isComplementaryAliasColors() + { + if(mComplementaryAliasColors == null) + { + mComplementaryAliasColors = mPreferences.getBoolean(PREFERENCE_KEY_COMPLEMENTARY_ALIAS_COLORS, false); + } + + return mComplementaryAliasColors; + } + + /** + * Sets the enabled state for using complementary alias colors in dark themes. + * @param enabled true to replace alias colors that lack contrast with the background. + */ + public void setComplementaryAliasColors(boolean enabled) + { + mComplementaryAliasColors = enabled; + mPreferences.putBoolean(PREFERENCE_KEY_COMPLEMENTARY_ALIAS_COLORS, enabled); + notifyPreferenceUpdated(); + } + /** * Returns the GUI zoom factor. 1.0 = 100% (default). Bounded to * [{@link #MIN_GUI_SCALE}, {@link #MAX_GUI_SCALE}].