diff --git a/sampler/src/main/java/atlantafx/sampler/event/ThemeEvent.java b/sampler/src/main/java/atlantafx/sampler/event/ThemeEvent.java new file mode 100644 index 0000000..98296c1 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/event/ThemeEvent.java @@ -0,0 +1,23 @@ +package atlantafx.sampler.event; + +public class ThemeEvent extends Event { + + private final EventType eventType; + + public ThemeEvent(EventType eventType) { + this.eventType = eventType; + } + + public EventType getEventType() { + return eventType; + } + + public enum EventType { + // theme can change both, base font size and colors + THEME_CHANGE, + // font size or family only change + FONT_CHANGE, + // colors only change + COLOR_CHANGE + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/layout/MainLayer.java b/sampler/src/main/java/atlantafx/sampler/layout/MainLayer.java index fd5f31c..7ca4c61 100644 --- a/sampler/src/main/java/atlantafx/sampler/layout/MainLayer.java +++ b/sampler/src/main/java/atlantafx/sampler/layout/MainLayer.java @@ -3,6 +3,7 @@ package atlantafx.sampler.layout; import atlantafx.sampler.page.Page; import atlantafx.sampler.page.components.OverviewPage; +import atlantafx.sampler.theme.ThemeManager; import javafx.animation.FadeTransition; import javafx.application.Platform; import javafx.scene.layout.BorderPane; diff --git a/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java b/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java index af13bbd..34b6b04 100755 --- a/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java @@ -149,6 +149,7 @@ public abstract class AbstractPage extends BorderPane implements Page { quickConfigPopover.setHeaderAlwaysVisible(false); quickConfigPopover.setDetachable(false); quickConfigPopover.setArrowLocation(TOP_CENTER); + quickConfigPopover.setOnShowing(e -> content.update()); } quickConfigPopover.show(quickConfigBtn); @@ -169,7 +170,7 @@ public abstract class AbstractPage extends BorderPane implements Page { // set syntax highlight theme according to JavaFX theme ThemeManager tm = ThemeManager.getInstance(); - codeViewer.setContent(stream, tm.getMatchingHighlightJSTheme(tm.getTheme())); + codeViewer.setContent(stream, tm.getMatchingSourceCodeHighlightTheme(tm.getTheme())); graphic.setIconCode(ICON_SAMPLE); codeViewerWrapper.toFront(); diff --git a/sampler/src/main/java/atlantafx/sampler/page/QuickConfigMenu.java b/sampler/src/main/java/atlantafx/sampler/page/QuickConfigMenu.java index 8d3084e..51f6992 100644 --- a/sampler/src/main/java/atlantafx/sampler/page/QuickConfigMenu.java +++ b/sampler/src/main/java/atlantafx/sampler/page/QuickConfigMenu.java @@ -2,12 +2,15 @@ package atlantafx.sampler.page; import atlantafx.base.controls.Spacer; +import atlantafx.sampler.page.general.AccentColorSelector; import atlantafx.sampler.theme.ThemeManager; import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.css.PseudoClass; import javafx.geometry.HorizontalDirection; +import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.Label; @@ -26,6 +29,8 @@ import java.util.Objects; import java.util.function.Consumer; import static atlantafx.base.theme.Styles.*; +import static atlantafx.sampler.theme.ThemeManager.DEFAULT_ZOOM; +import static atlantafx.sampler.theme.ThemeManager.SUPPORTED_ZOOM; import static javafx.geometry.Pos.CENTER_LEFT; import static org.kordamp.ikonli.material2.Material2AL.ARROW_BACK; import static org.kordamp.ikonli.material2.Material2AL.ARROW_FORWARD; @@ -43,14 +48,10 @@ public class QuickConfigMenu extends StackPane { private Runnable exitHandler; private final Consumer navHandler = s -> { - Pane pane = null; + Menu menu = null; switch (s) { - case MainMenu.ID -> pane = getOrCreateMainMenu(); - case ThemeSelectionMenu.ID -> { - ThemeSelectionMenu menu = getOrCreateThemeSelectionMenu(); - menu.update(); - pane = menu; - } + case MainMenu.ID -> menu = getOrCreateMainMenu(); + case ThemeSelectionMenu.ID -> menu = getOrCreateThemeSelectionMenu(); default -> { if (exitHandler != null) { exitHandler.run(); @@ -58,7 +59,10 @@ public class QuickConfigMenu extends StackPane { } } } - getChildren().setAll(Objects.requireNonNull(pane)); + + Objects.requireNonNull(menu); + menu.update(); + getChildren().setAll(menu.getRoot()); }; public void setExitHandler(Runnable exitHandler) { @@ -98,16 +102,30 @@ public class QuickConfigMenu extends StackPane { return root; } + public void update() { + getOrCreateMainMenu().update(); + } + /////////////////////////////////////////////////////////////////////////// - private static class MainMenu extends VBox { + private interface Menu { + + void update(); + + Pane getRoot(); + } + + private static class MainMenu extends VBox implements Menu { private static final String ID = "MainMenu"; - private static final List FONT_SCALE = List.of( - 50, 75, 80, 90, 100, 110, 125, 150, 175, 200 - ); - private final IntegerProperty fontScale = new SimpleIntegerProperty(100); + private final IntegerProperty zoom = new SimpleIntegerProperty(DEFAULT_ZOOM); + private final BooleanBinding canZoomIn = Bindings.createBooleanBinding( + () -> SUPPORTED_ZOOM.indexOf(zoom.get()) < SUPPORTED_ZOOM.size() - 1, zoom + ); + private final BooleanBinding canZoomOut = Bindings.createBooleanBinding( + () -> SUPPORTED_ZOOM.indexOf(zoom.get()) >= 1, zoom + ); public MainMenu(Consumer navHandler) { super(); @@ -117,54 +135,66 @@ public class QuickConfigMenu extends StackPane { var themeSelectionMenu = menu("Theme", HorizontalDirection.RIGHT); themeSelectionMenu.setOnMouseClicked(e -> navHandler.accept(ThemeSelectionMenu.ID)); + var accentSelector = new AccentColorSelector(); + accentSelector.setAlignment(Pos.CENTER); + // ~ var zoomInBtn = new Button("", new FontIcon(Feather.ZOOM_IN)); zoomInBtn.getStyleClass().addAll(BUTTON_CIRCLE, BUTTON_ICON, FLAT); zoomInBtn.setOnAction(e -> { - int idx = FONT_SCALE.indexOf(fontScale.get()); - if (idx < FONT_SCALE.size() - 1) { fontScale.set(FONT_SCALE.get(idx + 1)); } + if (canZoomIn.get()) { + zoom.set(SUPPORTED_ZOOM.get(SUPPORTED_ZOOM.indexOf(zoom.get()) + 1)); + } }); - zoomInBtn.disableProperty().bind(Bindings.createBooleanBinding( - () -> FONT_SCALE.indexOf(fontScale.get()) >= FONT_SCALE.size() - 1, fontScale) - ); + zoomInBtn.disableProperty().bind(canZoomIn.not()); var zoomOutBtn = new Button("", new FontIcon(Feather.ZOOM_OUT)); zoomOutBtn.getStyleClass().addAll(BUTTON_CIRCLE, BUTTON_ICON, FLAT); zoomOutBtn.setOnAction(e -> { - int idx = FONT_SCALE.indexOf(fontScale.get()); - if (idx >= 1) { fontScale.set(FONT_SCALE.get(idx - 1)); } + if (canZoomOut.get()) { + zoom.set(SUPPORTED_ZOOM.get(SUPPORTED_ZOOM.indexOf(zoom.get()) - 1)); + } }); - zoomOutBtn.disableProperty().bind(Bindings.createBooleanBinding( - () -> FONT_SCALE.indexOf(fontScale.get()) <= 0, fontScale) - ); + zoomOutBtn.disableProperty().bind(canZoomOut.not()); - // FIXME: Default zoom value is always 100% which isn't correct because it may have been changed earlier var zoomLabel = new Label(); - zoomLabel.textProperty().bind(Bindings.createStringBinding(() -> fontScale.get() + "%", fontScale)); + zoomLabel.textProperty().bind(Bindings.createStringBinding(() -> zoom.get() + "%", zoom)); var zoomBox = new HBox(zoomOutBtn, new Spacer(), zoomLabel, new Spacer(), zoomInBtn); zoomBox.setAlignment(CENTER_LEFT); zoomBox.getStyleClass().addAll("row"); final var tm = ThemeManager.getInstance(); - fontScale.addListener((obs, old, val) -> { - if (val != null) { - double fontSize = val.intValue() != 100 ? - ThemeManager.DEFAULT_FONT_SIZE / 100.0 * val.intValue() : - ThemeManager.DEFAULT_FONT_SIZE; - tm.setFontSize((int) Math.ceil(fontSize)); - tm.reloadCustomCSS(); + zoom.addListener((obs, old, val) -> { + if (val != null && tm.getZoom() != val.intValue()) { + tm.setZoom(val.intValue()); } }); // ~ - getChildren().setAll(themeSelectionMenu, new Separator(), zoomBox); + getChildren().setAll( + themeSelectionMenu, + new Separator(), + accentSelector, + new Separator(), + zoomBox + ); + } + + @Override + public void update() { + zoom.set(ThemeManager.getInstance().getZoom()); + } + + @Override + public Pane getRoot() { + return this; } } - private static class ThemeSelectionMenu extends VBox { + private static class ThemeSelectionMenu extends VBox implements Menu { public static final String ID = "ThemeSelectionMenu"; @@ -174,7 +204,7 @@ public class QuickConfigMenu extends StackPane { super(); Objects.requireNonNull(navHandler); - final var tm = ThemeManager.getInstance(); + var tm = ThemeManager.getInstance(); var mainMenu = menu("Theme", HorizontalDirection.LEFT); mainMenu.setOnMouseClicked(e -> navHandler.accept(MainMenu.ID)); @@ -189,7 +219,6 @@ public class QuickConfigMenu extends StackPane { item.setUserData(theme.getName()); item.setOnMouseClicked(e -> { tm.setTheme(theme); - tm.reloadCustomCSS(); navHandler.accept(MainMenu.ID); navHandler.accept(QuickConfigMenu.EXIT_ID); }); @@ -199,11 +228,17 @@ public class QuickConfigMenu extends StackPane { }); } + @Override public void update() { items.forEach(item -> item.pseudoClassStateChanged( SELECTED, Objects.equals(item.getUserData(), ThemeManager.getInstance().getTheme().getName()) )); } + + @Override + public Pane getRoot() { + return this; + } } } diff --git a/sampler/src/main/java/atlantafx/sampler/page/general/AccentColorSelector.java b/sampler/src/main/java/atlantafx/sampler/page/general/AccentColorSelector.java new file mode 100644 index 0000000..6e03377 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/general/AccentColorSelector.java @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.general; + +import atlantafx.sampler.theme.AccentColor; +import atlantafx.sampler.theme.ThemeManager; +import atlantafx.sampler.util.JColorUtils; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import org.kordamp.ikonli.javafx.FontIcon; +import org.kordamp.ikonli.material2.Material2AL; + +import static atlantafx.base.theme.Styles.BUTTON_ICON; +import static atlantafx.base.theme.Styles.FLAT; + +public class AccentColorSelector extends HBox { + + public AccentColorSelector() { + super(); + createView(); + } + + private void createView() { + var resetBtn = new Button("", new FontIcon(Material2AL.CLEAR)); + resetBtn.getStyleClass().addAll(BUTTON_ICON, FLAT); + resetBtn.setOnAction(e -> ThemeManager.getInstance().resetAccentColor()); + + setAlignment(Pos.CENTER_LEFT); + getChildren().setAll( + colorButton(AccentColor.PURPLE), + colorButton(AccentColor.PINK), + colorButton(AccentColor.CORAL), + resetBtn + ); + getStyleClass().add("accent-color-selector"); + } + + private Button colorButton(AccentColor accentColor) { + var icon = new Region(); + icon.getStyleClass().add("icon"); + + var colorMap = accentColor.getColorMap(); + + var btn = new Button("", icon); + btn.getStyleClass().addAll(BUTTON_ICON, FLAT, "color-button"); + btn.setStyle("-color-primary:" + JColorUtils.toHexWithAlpha(colorMap.getPrimaryColor()) + ";"); + btn.setUserData(accentColor); + btn.setOnAction(e -> ThemeManager.getInstance().setAccentColor((AccentColor) btn.getUserData())); + + return btn; + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/general/ContrastChecker.java b/sampler/src/main/java/atlantafx/sampler/page/general/ContrastChecker.java index 39a6cf3..dcc51a7 100644 --- a/sampler/src/main/java/atlantafx/sampler/page/general/ContrastChecker.java +++ b/sampler/src/main/java/atlantafx/sampler/page/general/ContrastChecker.java @@ -32,6 +32,7 @@ import org.kordamp.ikonli.feather.Feather; import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.material2.Material2AL; +import java.util.Map; import java.util.Objects; import static atlantafx.sampler.page.general.ColorPaletteBlock.validateColorName; @@ -148,10 +149,10 @@ class ContrastChecker extends GridPane { contrastRatio.addListener((obs, old, val) -> { if (val == null) { return; } float ratio = val.floatValue(); - updateWsagLabel(aaNormalLabel, ContrastLevel.AA_NORMAL.satisfies(ratio)); - updateWsagLabel(aaLargeLabel, ContrastLevel.AA_LARGE.satisfies(ratio)); - updateWsagLabel(aaaNormalLabel, ContrastLevel.AAA_NORMAL.satisfies(ratio)); - updateWsagLabel(aaaLargeLabel, ContrastLevel.AAA_LARGE.satisfies(ratio)); + updateContrastLevelLabel(aaNormalLabel, ContrastLevel.AA_NORMAL.satisfies(ratio)); + updateContrastLevelLabel(aaLargeLabel, ContrastLevel.AA_LARGE.satisfies(ratio)); + updateContrastLevelLabel(aaaNormalLabel, ContrastLevel.AAA_NORMAL.satisfies(ratio)); + updateContrastLevelLabel(aaaLargeLabel, ContrastLevel.AAA_LARGE.satisfies(ratio)); }); // ~ @@ -266,12 +267,10 @@ class ContrastChecker extends GridPane { }); var applyBtn = new Button("Apply"); - applyBtn.setOnAction(e -> { - var tm = ThemeManager.getInstance(); - tm.setColor(getBgColorName(), bgColor.getColor()); - tm.setColor(getFgColorName(), fgColor.getColor()); - tm.reloadCustomCSS(); - }); + applyBtn.setOnAction(e -> ThemeManager.getInstance().setNamedColors(Map.of( + getBgColorName(), bgColor.getColor(), + getFgColorName(), fgColor.getColor() + ))); var controlsBox = new HBox(20, new Spacer(), flattenBtn, applyBtn); controlsBox.setAlignment(Pos.CENTER_LEFT); @@ -321,8 +320,8 @@ class ContrastChecker extends GridPane { private void updateStyle() { setStyle(String.format("-color-contrast-checker-bg:%s;-color-contrast-checker-fg:%s;", - JColorUtils.toHexWithAlpha(bgColor.getColor()), - JColorUtils.toHexWithAlpha(getSafeFgColor()) + JColorUtils.toHexWithAlpha(bgColor.getColor()), + JColorUtils.toHexWithAlpha(getSafeFgColor()) )); } @@ -342,7 +341,7 @@ class ContrastChecker extends GridPane { fgAlphaSlider.setValue(color.getOpacity()); } - private void updateWsagLabel(Label label, boolean success) { + private void updateContrastLevelLabel(Label label, boolean success) { FontIcon icon = Objects.requireNonNull((FontIcon) label.getGraphic()); if (success) { label.setText(STATE_PASS); diff --git a/sampler/src/main/java/atlantafx/sampler/page/general/ThemePage.java b/sampler/src/main/java/atlantafx/sampler/page/general/ThemePage.java index 804f3ef..dcc9696 100755 --- a/sampler/src/main/java/atlantafx/sampler/page/general/ThemePage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/general/ThemePage.java @@ -2,8 +2,9 @@ package atlantafx.sampler.page.general; import atlantafx.base.theme.Theme; +import atlantafx.sampler.event.DefaultEventBus; +import atlantafx.sampler.event.ThemeEvent; import atlantafx.sampler.page.AbstractPage; -import atlantafx.sampler.theme.ThemeEvent.EventType; import atlantafx.sampler.theme.ThemeManager; import atlantafx.sampler.util.NodeUtils; import javafx.geometry.HPos; @@ -19,6 +20,8 @@ import java.net.URI; import java.util.Objects; import java.util.function.Consumer; +import static atlantafx.sampler.event.ThemeEvent.EventType.COLOR_CHANGE; +import static atlantafx.sampler.event.ThemeEvent.EventType.THEME_CHANGE; import static atlantafx.sampler.util.Controls.hyperlink; public class ThemePage extends AbstractPage { @@ -28,9 +31,9 @@ public class ThemePage extends AbstractPage { private final Consumer colorBlockActionHandler = colorBlock -> { ContrastCheckerDialog dialog = getOrCreateContrastCheckerDialog(); dialog.getContent().setValues(colorBlock.getFgColorName(), - colorBlock.getFgColor(), - colorBlock.getBgColorName(), - colorBlock.getBgColor() + colorBlock.getFgColor(), + colorBlock.getBgColorName(), + colorBlock.getBgColor() ); overlay.setContent(dialog, HPos.CENTER); overlay.toFront(); @@ -47,9 +50,8 @@ public class ThemePage extends AbstractPage { public ThemePage() { super(); createView(); - ThemeManager.getInstance().addEventListener(e -> { - if (e.eventType() == EventType.THEME_CHANGE || e.eventType() == EventType.CUSTOM_CSS_CHANGE) { - // only works for managed nodes + DefaultEventBus.getInstance().subscribe(ThemeEvent.class, e -> { + if (e.getEventType() == THEME_CHANGE || e.getEventType() == COLOR_CHANGE) { colorPalette.updateColorInfo(Duration.seconds(1)); colorScale.updateColorInfo(Duration.seconds(1)); } @@ -67,7 +69,7 @@ public class ThemePage extends AbstractPage { var noteText = new TextFlow( new Text("AtlantaFX follows "), hyperlink("Github Primer interface guidelines", - URI.create("https://primer.style/design/foundations/color") + URI.create("https://primer.style/design/foundations/color") ), new Text(" and color system.") ); @@ -89,6 +91,8 @@ public class ThemePage extends AbstractPage { ChoiceBox themeSelector = themeSelector(); themeSelector.setPrefWidth(200); + var accentSelector = new AccentColorSelector(); + // ~ var grid = new GridPane(); @@ -97,6 +101,8 @@ public class ThemePage extends AbstractPage { grid.add(new Label("Color theme"), 0, 0); grid.add(themeSelector, 1, 0); + grid.add(new Label("Accent color"), 0, 1); + grid.add(accentSelector, 1, 1); return grid; } @@ -109,7 +115,6 @@ public class ThemePage extends AbstractPage { if (val != null && getScene() != null) { var tm = ThemeManager.getInstance(); tm.setTheme(val); - tm.reloadCustomCSS(); } }); diff --git a/sampler/src/main/java/atlantafx/sampler/page/general/TypographyPage.java b/sampler/src/main/java/atlantafx/sampler/page/general/TypographyPage.java index b2a6bb7..3768c1a 100755 --- a/sampler/src/main/java/atlantafx/sampler/page/general/TypographyPage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/general/TypographyPage.java @@ -1,9 +1,10 @@ /* SPDX-License-Identifier: MIT */ package atlantafx.sampler.page.general; +import atlantafx.sampler.event.DefaultEventBus; +import atlantafx.sampler.event.ThemeEvent; import atlantafx.sampler.page.AbstractPage; import atlantafx.sampler.page.SampleBlock; -import atlantafx.sampler.theme.ThemeEvent.EventType; import atlantafx.sampler.theme.ThemeManager; import atlantafx.sampler.util.NodeUtils; import javafx.animation.KeyFrame; @@ -25,6 +26,9 @@ import javafx.scene.text.TextFlow; import javafx.util.Duration; import static atlantafx.base.theme.Styles.*; +import static atlantafx.sampler.event.ThemeEvent.EventType.FONT_CHANGE; +import static atlantafx.sampler.event.ThemeEvent.EventType.THEME_CHANGE; +import static atlantafx.sampler.theme.ThemeManager.SUPPORTED_FONT_SIZE; public class TypographyPage extends AbstractPage { @@ -41,9 +45,8 @@ public class TypographyPage extends AbstractPage { public TypographyPage() { super(); createView(); - ThemeManager.getInstance().addEventListener(e -> { - if (e.eventType() == EventType.FONT_FAMILY_CHANGE || e.eventType() == EventType.FONT_SIZE_CHANGE) { - // only works for managed nodes + DefaultEventBus.getInstance().subscribe(ThemeEvent.class, e -> { + if (e.getEventType() == THEME_CHANGE || e.getEventType() == FONT_CHANGE) { updateFontInfo(Duration.seconds(1)); } }); @@ -89,7 +92,6 @@ public class TypographyPage extends AbstractPage { comboBox.valueProperty().addListener((obs, old, val) -> { if (val != null) { tm.setFontFamily(DEFAULT_FONT_ID.equals(val) ? ThemeManager.DEFAULT_FONT_FAMILY_NAME : val); - tm.reloadCustomCSS(); } }); @@ -99,7 +101,11 @@ public class TypographyPage extends AbstractPage { private Spinner fontSizeSpinner() { final var tm = ThemeManager.getInstance(); - var spinner = new Spinner(10, 24, tm.getFontSize()); + var spinner = new Spinner( + SUPPORTED_FONT_SIZE.get(0), + SUPPORTED_FONT_SIZE.get(SUPPORTED_FONT_SIZE.size() - 1), + tm.getFontSize() + ); spinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); spinner.setPrefWidth(CONTROL_WIDTH); @@ -113,7 +119,6 @@ public class TypographyPage extends AbstractPage { spinner.valueProperty().addListener((obs, old, val) -> { if (val != null) { tm.setFontSize(val); - tm.reloadCustomCSS(); updateFontInfo(Duration.seconds(1)); } }); diff --git a/sampler/src/main/java/atlantafx/sampler/theme/AccentColor.java b/sampler/src/main/java/atlantafx/sampler/theme/AccentColor.java new file mode 100644 index 0000000..6c8ed30 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/theme/AccentColor.java @@ -0,0 +1,18 @@ +package atlantafx.sampler.theme; + +public enum AccentColor { + + PURPLE(ColorMap.primerPurple()), + PINK(ColorMap.primerPink()), + CORAL(ColorMap.primerCoral()); + + private final ColorMap colorMap; + + AccentColor(ColorMap colorMap) { + this.colorMap = colorMap; + } + + public ColorMap getColorMap() { + return colorMap; + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/theme/ColorMap.java b/sampler/src/main/java/atlantafx/sampler/theme/ColorMap.java new file mode 100644 index 0000000..0c09867 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/theme/ColorMap.java @@ -0,0 +1,146 @@ +package atlantafx.sampler.theme; + +import javafx.scene.paint.Color; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static atlantafx.sampler.util.JColorUtils.opaqueColor; + +// Theoretically, one should use different accent shades for each color theme +// and for both light and dark mode. But since creating color palettes is +// pretty time-consuming, dynamic color calculation based on opacity should +// suit demo purposes. +public class ColorMap { + + private static final String MUTED_COLOR_NAME = "-color-accent-muted"; + private static final String SUBTLE_COLOR_NAME = "-color-accent-subtle"; + + private final Map allColors = new HashMap<>(); + private final Map dynamicColors = new HashMap<>(); + + private final Color primaryColor; + + private ColorMap(Color primaryColor) { + this.primaryColor = primaryColor; + } + + public Color getPrimaryColor() { + return primaryColor; + } + + public Map getAll() { + return new HashMap<>(allColors); + } + + private void setColor(String colorName, Color colorValue) { + allColors.put(Objects.requireNonNull(colorName), colorValue); + } + + private void setDynamicColor(String colorName, Color colorValue) { + dynamicColors.put(Objects.requireNonNull(colorName), colorValue); + } + + void update(Color background) { + allColors.put(MUTED_COLOR_NAME, opaqueColor(background, dynamicColors.get(MUTED_COLOR_NAME), 0.5)); + allColors.put(SUBTLE_COLOR_NAME, opaqueColor(background, dynamicColors.get(SUBTLE_COLOR_NAME), 0.4)); + } + + /////////////////////////////////////////////////////////////////////////// + + public static ColorMap primerPurple() { + var map = new ColorMap(Color.web("#8250df")); + + map.setColor("-color-blue-0", Color.web("#fbefff")); + map.setColor("-color-blue-1", Color.web("#ecd8ff")); + map.setColor("-color-blue-2", Color.web("#d8b9ff")); + map.setColor("-color-blue-3", Color.web("#c297ff")); + map.setColor("-color-blue-4", Color.web("#a475f9")); + map.setColor("-color-blue-5", Color.web("#8250df")); + map.setColor("-color-blue-6", Color.web("#6639ba")); + map.setColor("-color-blue-7", Color.web("#512a97")); + map.setColor("-color-blue-8", Color.web("#3e1f79")); + map.setColor("-color-blue-9", Color.web("#2e1461")); + map.setColor("-color-accent-fg", Color.web("#8250df")); + map.setColor("-color-accent-emphasis", Color.web("#8250df")); + map.setColor(MUTED_COLOR_NAME, Color.web("#d8b9ff")); + map.setColor(SUBTLE_COLOR_NAME, Color.web("#fbefff")); + + map.setDynamicColor(MUTED_COLOR_NAME, Color.web("#d8b9ff")); + map.setDynamicColor(SUBTLE_COLOR_NAME, Color.web("#fbefff")); + + return map; + } + + public static ColorMap primerPink() { + var map = new ColorMap(Color.web("#bf3989")); + + map.setColor("-color-blue-0", Color.web("#ffeff7")); + map.setColor("-color-blue-1", Color.web("#ffd3eb")); + map.setColor("-color-blue-2", Color.web("#ffadda")); + map.setColor("-color-blue-3", Color.web("#ff80c8")); + map.setColor("-color-blue-4", Color.web("#e85aad")); + map.setColor("-color-blue-5", Color.web("#bf3989")); + map.setColor("-color-blue-6", Color.web("#99286e")); + map.setColor("-color-blue-7", Color.web("#772057")); + map.setColor("-color-blue-8", Color.web("#611347")); + map.setColor("-color-blue-9", Color.web("#4d0336")); + map.setColor("-color-accent-fg", Color.web("#bf3989")); + map.setColor("-color-accent-emphasis", Color.web("#bf3989")); + map.setColor(MUTED_COLOR_NAME, Color.web("#ffadda")); + map.setColor(SUBTLE_COLOR_NAME, Color.web("#ffeff7")); + + map.setDynamicColor(MUTED_COLOR_NAME, Color.web("#ffadda")); + map.setDynamicColor(SUBTLE_COLOR_NAME, Color.web("#ffeff7")); + + return map; + } + + public static ColorMap primerCoral() { + var map = new ColorMap(Color.web("#c4432b")); + + map.setColor("-color-blue-0", Color.web("#fff0eb")); + map.setColor("-color-blue-1", Color.web("#ffd6cc")); + map.setColor("-color-blue-2", Color.web("#ffb4a1")); + map.setColor("-color-blue-3", Color.web("#fd8c73")); + map.setColor("-color-blue-4", Color.web("#ec6547")); + map.setColor("-color-blue-5", Color.web("#c4432b")); + map.setColor("-color-blue-6", Color.web("#9e2f1c")); + map.setColor("-color-blue-7", Color.web("#801f0f")); + map.setColor("-color-blue-8", Color.web("#691105")); + map.setColor("-color-blue-9", Color.web("#510901")); + map.setColor("-color-accent-fg", Color.web("#c4432b")); + map.setColor("-color-accent-emphasis", Color.web("#c4432b")); + map.setColor(MUTED_COLOR_NAME, Color.web("#ffb4a1")); + map.setColor(SUBTLE_COLOR_NAME, Color.web("#fff0eb")); + + map.setDynamicColor(MUTED_COLOR_NAME, Color.web("#ffb4a1")); + map.setDynamicColor(SUBTLE_COLOR_NAME, Color.web("#fff0eb")); + + return map; + } + + // empty map contains only color names and used to reset + // accent color scale to its initial state + static ColorMap empty() { + var map = new ColorMap(Color.web("#bf3989")); + + map.setColor("-color-blue-0", null); + map.setColor("-color-blue-1", null); + map.setColor("-color-blue-2", null); + map.setColor("-color-blue-3", null); + map.setColor("-color-blue-4", null); + map.setColor("-color-blue-5", null); + map.setColor("-color-blue-6", null); + map.setColor("-color-blue-7", null); + map.setColor("-color-blue-8", null); + map.setColor("-color-blue-9", null); + map.setColor("-color-accent-fg", null); + map.setColor("-color-accent-emphasis", null); + map.setColor(MUTED_COLOR_NAME, null); + map.setColor(SUBTLE_COLOR_NAME, null); + + return map; + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/theme/ThemeEvent.java b/sampler/src/main/java/atlantafx/sampler/theme/ThemeEvent.java deleted file mode 100644 index 578313b..0000000 --- a/sampler/src/main/java/atlantafx/sampler/theme/ThemeEvent.java +++ /dev/null @@ -1,11 +0,0 @@ -package atlantafx.sampler.theme; - -public record ThemeEvent(EventType eventType) { - - public enum EventType { - THEME_CHANGE, - FONT_FAMILY_CHANGE, - FONT_SIZE_CHANGE, - CUSTOM_CSS_CHANGE - } -} diff --git a/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java b/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java index cd43341..b96cad3 100644 --- a/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java +++ b/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java @@ -4,7 +4,10 @@ package atlantafx.sampler.theme; import atlantafx.base.theme.*; import atlantafx.sampler.Launcher; import atlantafx.sampler.Resources; -import atlantafx.sampler.theme.ThemeEvent.EventType; +import atlantafx.sampler.event.DefaultEventBus; +import atlantafx.sampler.event.EventBus; +import atlantafx.sampler.event.ThemeEvent; +import atlantafx.sampler.event.ThemeEvent.EventType; import atlantafx.sampler.util.JColor; import javafx.application.Application; import javafx.css.PseudoClass; @@ -13,46 +16,67 @@ import javafx.scene.paint.Color; import java.net.URI; import java.util.*; -import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import static atlantafx.sampler.Resources.getResource; import static java.nio.charset.StandardCharsets.UTF_8; public final class ThemeManager { - private static final String DUMMY_STYLESHEET = Resources.getResource("assets/styles/empty.css").toString(); + private static final String DUMMY_STYLESHEET = getResource("assets/styles/empty.css").toString(); private static final PseudoClass USER_CUSTOM = PseudoClass.getPseudoClass("user-custom"); + private static final EventBus EVENT_BUS = DefaultEventBus.getInstance(); public static final String DEFAULT_FONT_FAMILY_NAME = "Inter"; public static final int DEFAULT_FONT_SIZE = 14; + public static final int DEFAULT_ZOOM = 100; + public static final AccentColor DEFAULT_ACCENT_COLOR = null; + public static final List SUPPORTED_FONT_SIZE = IntStream.range(8, 29).boxed().collect(Collectors.toList()); + public static final List SUPPORTED_ZOOM = List.of(50, 75, 80, 90, 100, 110, 125, 150, 175, 200); - // KEY | VALUE - // -fx-property | value; - private final Map customCSSDeclarations = new LinkedHashMap<>(); - // .foo | -fx-property: value; - private final Map customCSSRules = new LinkedHashMap<>(); + private final Map customCSSDeclarations = new LinkedHashMap<>(); // -fx-property | value; + private final Map customCSSRules = new LinkedHashMap<>(); // .foo | -fx-property: value; private Scene scene; private Theme currentTheme = null; private String fontFamily = DEFAULT_FONT_FAMILY_NAME; private int fontSize = DEFAULT_FONT_SIZE; - private final List> eventListeners = new ArrayList<>(); + private int zoom = DEFAULT_ZOOM; + private AccentColor accentColor = DEFAULT_ACCENT_COLOR; public Scene getScene() { return scene; } - public void addEventListener(Consumer listener) { - eventListeners.add(Objects.requireNonNull(listener)); - } - + // MUST BE SET ON STARTUP + // (this is supposed to be a constructor arg, but since app don't use DI..., sorry) public void setScene(Scene scene) { - this.scene = scene; + this.scene = Objects.requireNonNull(scene); } public Theme getTheme() { return currentTheme; } + public List getAvailableThemes() { + var themes = new ArrayList(); + var appStylesheets = new URI[] { URI.create(Resources.resolve("assets/styles/index.css")) }; + + if (Launcher.IS_DEV_MODE) { + themes.add(new ExternalTheme("Primer Light", DUMMY_STYLESHEET, merge(getResource("theme-test/primer-light.css"), appStylesheets), false)); + themes.add(new ExternalTheme("Primer Dark", DUMMY_STYLESHEET, merge(getResource("theme-test/primer-dark.css"), appStylesheets), true)); + themes.add(new ExternalTheme("Nord Light", DUMMY_STYLESHEET, merge(getResource("theme-test/nord-light.css"), appStylesheets), false)); + themes.add(new ExternalTheme("Nord Dark", DUMMY_STYLESHEET, merge(getResource("theme-test/nord-dark.css"), appStylesheets), true)); + } else { + themes.add(new PrimerLight(appStylesheets)); + themes.add(new PrimerDark(appStylesheets)); + themes.add(new NordLight(appStylesheets)); + themes.add(new NordDark(appStylesheets)); + } + return themes; + } + /** * Resets user agent stylesheet and then adds {@link Theme} styles to the {@link Scene} * stylesheets. This is necessary when we want to reload style changes at runtime, because @@ -62,7 +86,6 @@ public final class ThemeManager { * reason they won't be ignored when exactly the same stylesheet is set via {@link Scene#getStylesheets()}. */ public void setTheme(Theme theme) { - Objects.requireNonNull(scene); Objects.requireNonNull(theme); Application.setUserAgentStylesheet(Objects.requireNonNull(theme.getUserAgentStylesheet())); @@ -72,38 +95,10 @@ public final class ThemeManager { } theme.getStylesheets().forEach(uri -> scene.getStylesheets().add(uri.toString())); + resetCustomCSS(); + currentTheme = theme; - notifyEventListeners(EventType.THEME_CHANGE); - } - - public List getAvailableThemes() { - var themes = new ArrayList(); - var appStylesheets = new URI[] { URI.create(Resources.resolve("assets/styles/index.css")) }; - - if (Launcher.IS_DEV_MODE) { - themes.add(new ExternalTheme("Primer Light", DUMMY_STYLESHEET, merge( - Resources.getResource("theme-test/primer-light.css"), - appStylesheets - ), false)); - themes.add(new ExternalTheme("Primer Dark", DUMMY_STYLESHEET, merge( - Resources.getResource("theme-test/primer-dark.css"), - appStylesheets - ), true)); - themes.add(new ExternalTheme("Nord Light", DUMMY_STYLESHEET, merge( - Resources.getResource("theme-test/nord-light.css"), - appStylesheets - ), false)); - themes.add(new ExternalTheme("Nord Dark", DUMMY_STYLESHEET, merge( - Resources.getResource("theme-test/nord-dark.css"), - appStylesheets - ), true)); - } else { - themes.add(new PrimerLight(appStylesheets)); - themes.add(new PrimerDark(appStylesheets)); - themes.add(new NordLight(appStylesheets)); - themes.add(new NordDark(appStylesheets)); - } - return themes; + EVENT_BUS.publish(new ThemeEvent(EventType.THEME_CHANGE)); } public String getFontFamily() { @@ -113,8 +108,11 @@ public final class ThemeManager { public void setFontFamily(String fontFamily) { Objects.requireNonNull(fontFamily); setCustomDeclaration("-fx-font-family", "\"" + fontFamily + "\""); + this.fontFamily = fontFamily; - notifyEventListeners(EventType.FONT_FAMILY_CHANGE); + + reloadCustomCSS(); + EVENT_BUS.publish(new ThemeEvent(EventType.FONT_CHANGE)); } public boolean isDefaultFontFamily() { @@ -125,26 +123,109 @@ public final class ThemeManager { return fontSize; } - public void setFontSize(int fontSize) { - setCustomDeclaration("-fx-font-size", fontSize + "px"); - setCustomRule(".ikonli-font-icon", String.format("-fx-icon-size: %dpx;", fontSize + 2)); - this.fontSize = fontSize; - notifyEventListeners(EventType.FONT_SIZE_CHANGE); + public void setFontSize(int size) { + if (!SUPPORTED_FONT_SIZE.contains(size)) { + throw new IllegalArgumentException(String.format("Font size must in the range %d-%dpx. Actual value is %d.", + SUPPORTED_FONT_SIZE.get(0), + SUPPORTED_FONT_SIZE.get(SUPPORTED_FONT_SIZE.size() - 1), + size + )); + } + + setCustomDeclaration("-fx-font-size", size + "px"); + setCustomRule(".ikonli-font-icon", String.format("-fx-icon-size: %dpx;", size + 2)); + + this.fontSize = size; + + var rawZoom = (int) Math.ceil((size * 1.0 / DEFAULT_FONT_SIZE) * 100); + this.zoom = SUPPORTED_ZOOM.stream() + .min(Comparator.comparingInt(i -> Math.abs(i - rawZoom))) + .orElseThrow(NoSuchElementException::new); + + reloadCustomCSS(); + EVENT_BUS.publish(new ThemeEvent(EventType.FONT_CHANGE)); } - public void setColor(String colorName, Color color) { - Objects.requireNonNull(colorName); + public boolean isDefaultSize() { + return DEFAULT_FONT_SIZE == fontSize; + } + + public int getZoom() { + return zoom; + } + + public void setZoom(int zoom) { + if (!SUPPORTED_ZOOM.contains(zoom)) { + throw new IllegalArgumentException(String.format("Zoom value must one of %s. Actual value is %d.", + SUPPORTED_ZOOM, + zoom + )); + } + + setFontSize((int) Math.ceil(zoom != 100 ? (DEFAULT_FONT_SIZE * zoom) / 100.0f : DEFAULT_FONT_SIZE)); + this.zoom = zoom; + } + + public AccentColor getAccentColor() { + return accentColor; + } + + public void setAccentColor(AccentColor color) { Objects.requireNonNull(color); - setCustomDeclaration(colorName, JColor.color( - (float) color.getRed(), (float) color.getGreen(), (float) color.getBlue(), (float) color.getOpacity()).getColorHexWithAlpha() - ); + + var colorMap = color.getColorMap(); + + // adapt color map to the current theme + if (!getTheme().isDarkMode()) { + colorMap.update(Color.WHITE); + } else { + colorMap.update(Color.BLACK); + } + + applyColorMap(colorMap); + + this.accentColor = color; + + reloadCustomCSS(); + EVENT_BUS.publish(new ThemeEvent(EventType.COLOR_CHANGE)); } - public void resetColor(String colorName) { - Objects.requireNonNull(colorName); - removeCustomDeclaration(colorName); + public void resetAccentColor() { + applyColorMap(ColorMap.empty()); + this.accentColor = null; + + reloadCustomCSS(); + EVENT_BUS.publish(new ThemeEvent(EventType.COLOR_CHANGE)); } + public void setNamedColors(Map colors) { + Objects.requireNonNull(colors).forEach(this::setOrRemoveColor); + reloadCustomCSS(); + EVENT_BUS.publish(new ThemeEvent(EventType.COLOR_CHANGE)); + } + + public void unsetNamedColors(String... colors) { + for (String c : colors) { + setOrRemoveColor(c, null); + } + reloadCustomCSS(); + EVENT_BUS.publish(new ThemeEvent(EventType.COLOR_CHANGE)); + } + + public void resetAllChanges() { + resetCustomCSS(); + EVENT_BUS.publish(new ThemeEvent(EventType.THEME_CHANGE)); + } + + public HighlightJSTheme getMatchingSourceCodeHighlightTheme(Theme theme) { + Objects.requireNonNull(theme); + if ("Nord Light".equals(theme.getName())) { return HighlightJSTheme.nordLight(); } + if ("Nord Dark".equals(theme.getName())) { return HighlightJSTheme.nordDark(); } + return theme.isDarkMode() ? HighlightJSTheme.githubDark() : HighlightJSTheme.githubLight(); + } + + /////////////////////////////////////////////////////////////////////////// + private void setCustomDeclaration(String property, String value) { customCSSDeclarations.put(property, value); } @@ -161,7 +242,25 @@ public final class ThemeManager { customCSSRules.remove(selector); } - public void reloadCustomCSS() { + private void setOrRemoveColor(String colorName, Color color) { + Objects.requireNonNull(colorName); + if (color != null) { + setCustomDeclaration(colorName, JColor.color( + (float) color.getRed(), + (float) color.getGreen(), + (float) color.getBlue(), + (float) color.getOpacity()).getColorHexWithAlpha() + ); + } else { + removeCustomDeclaration(colorName); + } + } + + private void applyColorMap(ColorMap colorMap) { + colorMap.getAll().forEach(this::setOrRemoveColor); + } + + private void reloadCustomCSS() { Objects.requireNonNull(scene); StringBuilder css = new StringBuilder(); @@ -192,26 +291,12 @@ public final class ThemeManager { "data:text/css;base64," + Base64.getEncoder().encodeToString(css.toString().getBytes(UTF_8)) ); scene.getRoot().pseudoClassStateChanged(USER_CUSTOM, true); - notifyEventListeners(EventType.CUSTOM_CSS_CHANGE); } public void resetCustomCSS() { customCSSDeclarations.clear(); customCSSRules.clear(); scene.getRoot().pseudoClassStateChanged(USER_CUSTOM, false); - notifyEventListeners(EventType.CUSTOM_CSS_CHANGE); - } - - public HighlightJSTheme getMatchingHighlightJSTheme(Theme theme) { - Objects.requireNonNull(theme); - if ("Nord Light".equals(theme.getName())) { return HighlightJSTheme.nordLight(); } - if ("Nord Dark".equals(theme.getName())) { return HighlightJSTheme.nordDark(); } - return theme.isDarkMode() ? HighlightJSTheme.githubDark() : HighlightJSTheme.githubLight(); - } - - public void notifyEventListeners(EventType eventType) { - var e = new ThemeEvent(eventType); - eventListeners.forEach(l -> l.accept(e)); } @SafeVarargs diff --git a/sampler/src/main/java/atlantafx/sampler/util/JColorUtils.java b/sampler/src/main/java/atlantafx/sampler/util/JColorUtils.java index 254bd6c..a330880 100644 --- a/sampler/src/main/java/atlantafx/sampler/util/JColorUtils.java +++ b/sampler/src/main/java/atlantafx/sampler/util/JColorUtils.java @@ -26,7 +26,6 @@ package atlantafx.sampler.util; import javafx.scene.paint.Color; -import java.util.Arrays; import java.util.regex.Pattern; /** @@ -803,13 +802,13 @@ public class JColorUtils { *
* Source. */ - public static double[] flattenColor(Color bgColor, Color fgColor) { + public static double[] flattenColor(Color bg, Color fgColor) { var opacity = fgColor.getOpacity(); return opacity < 1 ? new double[] { - opacity * fgColor.getRed() + (1 - opacity) * bgColor.getRed(), - opacity * fgColor.getGreen() + (1 - opacity) * bgColor.getGreen(), - opacity * fgColor.getBlue() + (1 - opacity) * bgColor.getBlue(), + opacity * fgColor.getRed() + (1 - opacity) * bg.getRed(), + opacity * fgColor.getGreen() + (1 - opacity) * bg.getGreen(), + opacity * fgColor.getBlue() + (1 - opacity) * bg.getBlue(), } : new double[] { fgColor.getRed(), @@ -818,6 +817,19 @@ public class JColorUtils { }; } + /** + * The opposite to the {@link JColorUtils#flattenColor(Color, Color)}. It converts target opaque color + * to its equivalent with the desired opacity level. + */ + public static Color opaqueColor(Color bgColor, Color targetColor, double targetOpacity) { + return Color.color( + bgColor.getRed() + (targetColor.getRed() - bgColor.getRed()) * targetOpacity, + bgColor.getGreen() + (targetColor.getGreen() - bgColor.getGreen()) * targetOpacity, + bgColor.getBlue() + (targetColor.getBlue() - bgColor.getBlue()) * targetOpacity, + targetOpacity + ); + } + public static float[] toHSL(Color color) { return JColorUtils.toHSL( (float) color.getRed(), diff --git a/sampler/src/main/resources/assets/styles/scss/widgets/_accent-color-selector.scss b/sampler/src/main/resources/assets/styles/scss/widgets/_accent-color-selector.scss new file mode 100644 index 0000000..86fca5d --- /dev/null +++ b/sampler/src/main/resources/assets/styles/scss/widgets/_accent-color-selector.scss @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +.accent-color-selector { + -color-primary: -color-accent-emphasis; + + -fx-spacing: 1em; + + >.color-button { + >.icon { + -fx-min-width: 1em; + -fx-pref-width: 1em; + -fx-max-height: 1em; + -fx-min-width: 1em; + -fx-pref-height: 1em; + -fx-max-height: 1em; + -fx-background-color: -color-primary; + } + } +} \ No newline at end of file diff --git a/sampler/src/main/resources/assets/styles/scss/widgets/_color-palette.scss b/sampler/src/main/resources/assets/styles/scss/widgets/_color-palette.scss index b016623..adab062 100644 --- a/sampler/src/main/resources/assets/styles/scss/widgets/_color-palette.scss +++ b/sampler/src/main/resources/assets/styles/scss/widgets/_color-palette.scss @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -$color-wsag-bg-passed: #388e3c; -$color-wsag-bg-failed: #ef5350; -$color-wsag-fg: white; +$color-wcag-bg-passed: #388e3c; +$color-wcag-bg-failed: #ef5350; +$color-wcag-fg: white; #color-palette { @@ -24,18 +24,18 @@ $color-wsag-fg: white; -fx-cursor: hand; &:passed>.contrast-level-label { - -fx-background-color: $color-wsag-bg-passed; + -fx-background-color: $color-wcag-bg-passed; } >.contrast-level-label { - -fx-text-fill: $color-wsag-fg; - -fx-background-color: $color-wsag-bg-failed; + -fx-text-fill: $color-wcag-fg; + -fx-background-color: $color-wcag-bg-failed; -fx-background-radius: 6px; -fx-padding: 3px; >.ikonli-font-icon { - -fx-fill: $color-wsag-fg; - -fx-icon-color: $color-wsag-fg; + -fx-fill: $color-wcag-fg; + -fx-icon-color: $color-wcag-fg; } } } diff --git a/sampler/src/main/resources/assets/styles/scss/widgets/_contrast-checker.scss b/sampler/src/main/resources/assets/styles/scss/widgets/_contrast-checker.scss index 9abdd24..4eebca2 100644 --- a/sampler/src/main/resources/assets/styles/scss/widgets/_contrast-checker.scss +++ b/sampler/src/main/resources/assets/styles/scss/widgets/_contrast-checker.scss @@ -79,17 +79,17 @@ .contrast-level { >.state { -fx-padding: 0.5em 1em 0.5em 1em; - -fx-background-color: palette.$color-wsag-bg-failed; + -fx-background-color: palette.$color-wcag-bg-failed; -fx-background-radius: 4px; - -fx-text-fill: palette.$color-wsag-fg; + -fx-text-fill: palette.$color-wcag-fg; &:passed { - -fx-background-color: palette.$color-wsag-bg-passed; + -fx-background-color: palette.$color-wcag-bg-passed; } >.ikonli-font-icon { - -fx-fill: palette.$color-wsag-fg; - -fx-icon-color: palette.$color-wsag-fg; + -fx-fill: palette.$color-wcag-fg; + -fx-icon-color: palette.$color-wcag-fg; } } } diff --git a/sampler/src/main/resources/assets/styles/scss/widgets/_index.scss b/sampler/src/main/resources/assets/styles/scss/widgets/_index.scss index 69d84fa..441c016 100644 --- a/sampler/src/main/resources/assets/styles/scss/widgets/_index.scss +++ b/sampler/src/main/resources/assets/styles/scss/widgets/_index.scss @@ -3,4 +3,5 @@ @use "color-palette"; @use "color-scale"; @use "contrast-checker"; +@use "accent-color-selector"; @use "quick-config-menu"; \ No newline at end of file