Improve color contrast checker
Add option to remove color opacity (flatten).
This commit is contained in:
parent
197618ef2e
commit
2d6393cc2b
@ -1,7 +1,9 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
package atlantafx.sampler.page.general;
|
package atlantafx.sampler.page.general;
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.sampler.util.Containers;
|
import atlantafx.sampler.util.Containers;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
@ -23,6 +25,7 @@ class ColorBlock extends VBox {
|
|||||||
private final String fgColorName;
|
private final String fgColorName;
|
||||||
private final String bgColorName;
|
private final String bgColorName;
|
||||||
private final String borderColorName;
|
private final String borderColorName;
|
||||||
|
private final ReadOnlyObjectProperty<Color> bgBaseColor;
|
||||||
|
|
||||||
private final AnchorPane colorBox;
|
private final AnchorPane colorBox;
|
||||||
private final Text fgText;
|
private final Text fgText;
|
||||||
@ -32,10 +35,14 @@ class ColorBlock extends VBox {
|
|||||||
|
|
||||||
private Consumer<ColorBlock> actionHandler;
|
private Consumer<ColorBlock> actionHandler;
|
||||||
|
|
||||||
public ColorBlock(String fgColorName, String bgColorName, String borderColorName) {
|
public ColorBlock(String fgColorName,
|
||||||
|
String bgColorName,
|
||||||
|
String borderColorName,
|
||||||
|
ReadOnlyObjectProperty<Color> bgBaseColor) {
|
||||||
this.fgColorName = validateColorName(fgColorName);
|
this.fgColorName = validateColorName(fgColorName);
|
||||||
this.bgColorName = validateColorName(bgColorName);
|
this.bgColorName = validateColorName(bgColorName);
|
||||||
this.borderColorName = validateColorName(borderColorName);
|
this.borderColorName = validateColorName(borderColorName);
|
||||||
|
this.bgBaseColor = bgBaseColor;
|
||||||
|
|
||||||
fgText = new Text();
|
fgText = new Text();
|
||||||
fgText.setStyle("-fx-fill:" + fgColorName + ";");
|
fgText.setStyle("-fx-fill:" + fgColorName + ";");
|
||||||
@ -58,11 +65,15 @@ class ColorBlock extends VBox {
|
|||||||
colorBox.getStyleClass().add("box");
|
colorBox.getStyleClass().add("box");
|
||||||
colorBox.getChildren().setAll(fgText, wsagLabel, expandIcon);
|
colorBox.getChildren().setAll(fgText, wsagLabel, expandIcon);
|
||||||
colorBox.setOnMouseEntered(e -> {
|
colorBox.setOnMouseEntered(e -> {
|
||||||
toggleHover(true);
|
|
||||||
var bgFill = getBgColor();
|
var bgFill = getBgColor();
|
||||||
|
|
||||||
|
// this happens when css isn't updated yet
|
||||||
|
if (bgFill == null) { return; }
|
||||||
|
|
||||||
|
toggleHover(true);
|
||||||
// doesn't play quite well with transparency, because we not calc
|
// doesn't play quite well with transparency, because we not calc
|
||||||
// actual underlying background color to flatten bgFill
|
// actual underlying background color to flatten bgFill
|
||||||
expandIcon.setFill(getColorLuminance(flattenColor(Color.WHITE, bgFill)) < LUMINANCE_THRESHOLD ?
|
expandIcon.setFill(getColorLuminance(flattenColor(bgBaseColor.get(), bgFill)) < LUMINANCE_THRESHOLD ?
|
||||||
Color.WHITE : Color.BLACK
|
Color.WHITE : Color.BLACK
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -94,6 +105,12 @@ class ColorBlock extends VBox {
|
|||||||
wsagLabel.setOpacity(state ? 0.5 : 1);
|
wsagLabel.setOpacity(state ? 0.5 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Text description(String text) {
|
||||||
|
var t = new Text(text);
|
||||||
|
t.getStyleClass().addAll("description", Styles.TEXT_SMALL);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
var fgFill = getFgColor();
|
var fgFill = getFgColor();
|
||||||
var bgFill = getBgColor();
|
var bgFill = getBgColor();
|
||||||
@ -105,7 +122,7 @@ class ColorBlock extends VBox {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double contrastRatio = 1 / getContrastRatioOpacityAware(bgFill, fgFill);
|
double contrastRatio = 1 / getContrastRatioOpacityAware(bgFill, fgFill, bgBaseColor.get());
|
||||||
colorBox.pseudoClassStateChanged(PASSED, contrastRatio >= 4.5);
|
colorBox.pseudoClassStateChanged(PASSED, contrastRatio >= 4.5);
|
||||||
|
|
||||||
wsagIcon.setIconCode(contrastRatio >= 4.5 ? Material2AL.CHECK : Material2AL.CLOSE);
|
wsagIcon.setIconCode(contrastRatio >= 4.5 ? Material2AL.CHECK : Material2AL.CLOSE);
|
||||||
@ -119,9 +136,8 @@ class ColorBlock extends VBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Color getBgColor() {
|
public Color getBgColor() {
|
||||||
return colorBox.getBackground() != null & !colorBox.getBackground().isEmpty() ?
|
return colorBox.getBackground() != null && !colorBox.getBackground().isEmpty() ?
|
||||||
(Color) colorBox.getBackground().getFills().get(0).getFill() :
|
(Color) colorBox.getBackground().getFills().get(0).getFill() : null;
|
||||||
null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFgColorName() {
|
public String getFgColorName() {
|
||||||
@ -139,10 +155,4 @@ class ColorBlock extends VBox {
|
|||||||
public void setOnAction(Consumer<ColorBlock> actionHandler) {
|
public void setOnAction(Consumer<ColorBlock> actionHandler) {
|
||||||
this.actionHandler = actionHandler;
|
this.actionHandler = actionHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Text description(String text) {
|
|
||||||
var t = new Text(text);
|
|
||||||
t.getStyleClass().addAll("description", Styles.TEXT_SMALL);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
package atlantafx.sampler.page.general;
|
package atlantafx.sampler.page.general;
|
||||||
|
|
||||||
import atlantafx.base.controls.CustomTextField;
|
import atlantafx.base.controls.CustomTextField;
|
||||||
@ -12,11 +13,14 @@ import javafx.collections.FXCollections;
|
|||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.css.PseudoClass;
|
import javafx.css.PseudoClass;
|
||||||
|
import javafx.event.Event;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ContentDisplay;
|
import javafx.scene.control.ContentDisplay;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Slider;
|
import javafx.scene.control.Slider;
|
||||||
|
import javafx.scene.input.ContextMenuEvent;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
@ -36,6 +40,7 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
static final PseudoClass PASSED = PseudoClass.getPseudoClass("passed");
|
static final PseudoClass PASSED = PseudoClass.getPseudoClass("passed");
|
||||||
static final float[] COLOR_WHITE = new float[] { 255f, 255f, 255f, 1f };
|
static final float[] COLOR_WHITE = new float[] { 255f, 255f, 255f, 1f };
|
||||||
static final float[] COLOR_BLACK = new float[] { 0f, 0f, 0f, 1f };
|
static final float[] COLOR_BLACK = new float[] { 0f, 0f, 0f, 1f };
|
||||||
|
static final double CONTRAST_RATIO_THRESHOLD = 1.5;
|
||||||
static final double LUMINANCE_THRESHOLD = 0.55;
|
static final double LUMINANCE_THRESHOLD = 0.55;
|
||||||
|
|
||||||
private static final int SLIDER_WIDTH = 300;
|
private static final int SLIDER_WIDTH = 300;
|
||||||
@ -45,11 +50,8 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
|
|
||||||
private final ObservableHSLAColor bgColor = new ObservableHSLAColor(Color.WHITE);
|
private final ObservableHSLAColor bgColor = new ObservableHSLAColor(Color.WHITE);
|
||||||
private final ObservableHSLAColor fgColor = new ObservableHSLAColor(Color.BLACK);
|
private final ObservableHSLAColor fgColor = new ObservableHSLAColor(Color.BLACK);
|
||||||
private final DoubleBinding contrastRatio = Bindings.createDoubleBinding(
|
private final ReadOnlyObjectProperty<Color> bgBaseColor;
|
||||||
() -> 1 / getContrastRatioOpacityAware(bgColor.getColor(), fgColor.getColor()),
|
private final DoubleBinding contrastRatio;
|
||||||
bgColor.colorProperty(),
|
|
||||||
fgColor.colorProperty()
|
|
||||||
);
|
|
||||||
|
|
||||||
private Label bgColorNameLabel;
|
private Label bgColorNameLabel;
|
||||||
private Label fgColorNameLabel;
|
private Label fgColorNameLabel;
|
||||||
@ -62,8 +64,17 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
private Slider fgLightnessSlider;
|
private Slider fgLightnessSlider;
|
||||||
private Slider fgAlphaSlider;
|
private Slider fgAlphaSlider;
|
||||||
|
|
||||||
public ColorContrastChecker() {
|
public ColorContrastChecker(ReadOnlyObjectProperty<Color> bgBaseColor) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this.bgBaseColor = bgBaseColor;
|
||||||
|
this.contrastRatio = Bindings.createDoubleBinding(
|
||||||
|
() -> 1 / getContrastRatioOpacityAware(bgColor.getColor(), fgColor.getColor(), bgBaseColor.get()),
|
||||||
|
bgColor.colorProperty(),
|
||||||
|
fgColor.colorProperty(),
|
||||||
|
bgBaseColor
|
||||||
|
);
|
||||||
|
|
||||||
createView();
|
createView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +166,7 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
bgTextField.textProperty().bind(Bindings.createStringBinding(
|
bgTextField.textProperty().bind(Bindings.createStringBinding(
|
||||||
() -> bgColor.getColorHexWithAlpha().substring(1), bgColor.colorProperty()
|
() -> bgColor.getColorHexWithAlpha().substring(1), bgColor.colorProperty()
|
||||||
));
|
));
|
||||||
|
bgTextField.addEventFilter(ContextMenuEvent.CONTEXT_MENU_REQUESTED, Event::consume);
|
||||||
|
|
||||||
fgColorNameLabel = new Label("Foreground Color");
|
fgColorNameLabel = new Label("Foreground Color");
|
||||||
fgColorNameLabel.setPadding(new Insets(-15, 0, 0, 0));
|
fgColorNameLabel.setPadding(new Insets(-15, 0, 0, 0));
|
||||||
@ -166,6 +178,7 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
fgTextField.textProperty().bind(Bindings.createStringBinding(
|
fgTextField.textProperty().bind(Bindings.createStringBinding(
|
||||||
() -> fgColor.getColorHexWithAlpha().substring(1), fgColor.colorProperty()
|
() -> fgColor.getColorHexWithAlpha().substring(1), fgColor.colorProperty()
|
||||||
));
|
));
|
||||||
|
fgTextField.addEventFilter(ContextMenuEvent.CONTEXT_MENU_REQUESTED, Event::consume);
|
||||||
|
|
||||||
bgHueSlider = slider(1, 360, 1, 1);
|
bgHueSlider = slider(1, 360, 1, 1);
|
||||||
bgHueSlider.valueProperty().addListener((obs, old, val) -> {
|
bgHueSlider.valueProperty().addListener((obs, old, val) -> {
|
||||||
@ -243,6 +256,17 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
|
|
||||||
// ~
|
// ~
|
||||||
|
|
||||||
|
var flattenButton = new Button("Flatten");
|
||||||
|
flattenButton.setOnAction(e -> {
|
||||||
|
double[] flatBg = flattenColor(bgBaseColor.get(), bgColor.getColor());
|
||||||
|
setBackground(Color.color(flatBg[0], flatBg[1], flatBg[2]));
|
||||||
|
|
||||||
|
double[] flatFg = flattenColor(bgBaseColor.get(), fgColor.getColor());
|
||||||
|
setForeground(Color.color(flatFg[0], flatFg[1], flatFg[2]));
|
||||||
|
});
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
getStyleClass().add("contrast-checker");
|
getStyleClass().add("contrast-checker");
|
||||||
|
|
||||||
// column 0
|
// column 0
|
||||||
@ -259,6 +283,8 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
add(bgAlphaLabel, 0, 10);
|
add(bgAlphaLabel, 0, 10);
|
||||||
add(bgAlphaSlider, 0, 11);
|
add(bgAlphaSlider, 0, 11);
|
||||||
|
|
||||||
|
add(flattenButton, 0, 12);
|
||||||
|
|
||||||
// column 1
|
// column 1
|
||||||
add(wsagBox, 1, 0);
|
add(wsagBox, 1, 0);
|
||||||
add(new Label("Foreground Color"), 1, 1);
|
add(new Label("Foreground Color"), 1, 1);
|
||||||
@ -287,14 +313,28 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
float[] fg = fgColor.getRGBAColor();
|
float[] fg = fgColor.getRGBAColor();
|
||||||
|
|
||||||
// use fallback color if contrast ratio is too low
|
// use fallback color if contrast ratio is too low
|
||||||
if (contrastRatio.get() <= LUMINANCE_THRESHOLD) {
|
if (contrastRatio.get() <= CONTRAST_RATIO_THRESHOLD) {
|
||||||
fg = getColorLuminance(flattenColor(Color.WHITE, bgColor.getColor())) < 0.55 ? COLOR_WHITE : COLOR_BLACK;
|
fg = getColorLuminance(flattenColor(bgBaseColor.get(), bgColor.getColor())) < LUMINANCE_THRESHOLD ?
|
||||||
|
COLOR_WHITE :
|
||||||
|
COLOR_BLACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStyle(String.format("-color-contrast-checker-bg:rgba(%.0f,%.0f,%.0f,%.2f);-color-contrast-checker-fg:rgba(%.0f,%.0f,%.0f,%.2f);",
|
// flat colors are necessary for controls that use reverse styling (bg color over fg color),
|
||||||
|
// it won't be readable if we not remove transparency first
|
||||||
|
double[] bgFlat = flattenColor(bgBaseColor.get(), bgColor.getColor());
|
||||||
|
double[] fgFlat = flattenColor(bgBaseColor.get(), fgColor.getColor());
|
||||||
|
|
||||||
|
var style = String.format("-color-contrast-checker-bg:rgba(%.0f,%.0f,%.0f,%.2f);" +
|
||||||
|
"-color-contrast-checker-fg:rgba(%.0f,%.0f,%.0f,%.2f);" +
|
||||||
|
"-color-contrast-checker-bg-flat:%s;" +
|
||||||
|
"-color-contrast-checker-fg-flat:%s;",
|
||||||
bg[0], bg[1], bg[2], bg[3],
|
bg[0], bg[1], bg[2], bg[3],
|
||||||
fg[0], fg[1], fg[2], fg[3]
|
fg[0], fg[1], fg[2], fg[3],
|
||||||
));
|
JColor.color((float) bgFlat[0], (float) bgFlat[1], (float) bgFlat[2]).getColorHex(),
|
||||||
|
JColor.color((float) fgFlat[0], (float) fgFlat[1], (float) fgFlat[2]).getColorHex()
|
||||||
|
);
|
||||||
|
|
||||||
|
setStyle(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBackground(Color color) {
|
private void setBackground(Color color) {
|
||||||
@ -357,9 +397,9 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
return slider;
|
return slider;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double getContrastRatioOpacityAware(Color bgColor, Color fgColor) {
|
static double getContrastRatioOpacityAware(Color bgColor, Color fgColor, Color bgBaseColor) {
|
||||||
double luminance1 = getColorLuminance(flattenColor(Color.WHITE, bgColor));
|
double luminance1 = getColorLuminance(flattenColor(bgBaseColor, bgColor));
|
||||||
double luminance2 = getColorLuminance(flattenColor(Color.WHITE, fgColor));
|
double luminance2 = getColorLuminance(flattenColor(bgBaseColor, fgColor));
|
||||||
return getContrastRatio(luminance1, luminance2);
|
return getContrastRatio(luminance1, luminance2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,11 +411,11 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
private final ReadOnlyObjectWrapper<Color> color = new ReadOnlyObjectWrapper<>() { };
|
private final ReadOnlyObjectWrapper<Color> color = new ReadOnlyObjectWrapper<>() { };
|
||||||
|
|
||||||
public ObservableHSLAColor(Color initialColor) {
|
public ObservableHSLAColor(Color initialColor) {
|
||||||
color.set(initialColor);
|
|
||||||
values.addListener((ListChangeListener<Float>) c -> {
|
values.addListener((ListChangeListener<Float>) c -> {
|
||||||
float[] rgb = getRGBAArithmeticColor();
|
float[] rgb = getRGBAArithmeticColor();
|
||||||
color.set(Color.color(rgb[0], rgb[1], rgb[2], getAlpha()));
|
color.set(Color.color(rgb[0], rgb[1], rgb[2], getAlpha()));
|
||||||
});
|
});
|
||||||
|
setColor(initialColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getColor() {
|
public Color getColor() {
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
package atlantafx.sampler.page.general;
|
package atlantafx.sampler.page.general;
|
||||||
|
|
||||||
import atlantafx.base.controls.Spacer;
|
import atlantafx.base.controls.Spacer;
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
@ -10,6 +14,7 @@ import javafx.scene.layout.GridPane;
|
|||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
@ -22,14 +27,35 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
class ColorPalette extends VBox {
|
class ColorPalette extends VBox {
|
||||||
|
|
||||||
private final List<ColorBlock> blocks = new ArrayList<>();
|
|
||||||
|
|
||||||
private Label headerLabel;
|
private Label headerLabel;
|
||||||
private Button backBtn;
|
private Button backBtn;
|
||||||
private GridPane colorGrid;
|
private GridPane colorGrid;
|
||||||
private ColorContrastChecker contrastChecker;
|
private ColorContrastChecker contrastChecker;
|
||||||
private VBox contrastCheckerArea;
|
private VBox contrastCheckerArea;
|
||||||
|
|
||||||
|
private final List<ColorBlock> blocks = new ArrayList<>();
|
||||||
|
private final Consumer<ColorBlock> colorBlockActionHandler = colorBlock -> {
|
||||||
|
ColorContrastChecker c = getOrCreateContrastChecker();
|
||||||
|
c.setValues(colorBlock.getFgColorName(),
|
||||||
|
colorBlock.getFgColor(),
|
||||||
|
colorBlock.getBgColorName(),
|
||||||
|
colorBlock.getBgColor()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (contrastCheckerArea.getChildren().isEmpty()) {
|
||||||
|
contrastCheckerArea.getChildren().setAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
showContrastChecker();
|
||||||
|
};
|
||||||
|
|
||||||
|
private final ReadOnlyBooleanWrapper contrastCheckerActive = new ReadOnlyBooleanWrapper(false);
|
||||||
|
private final ReadOnlyObjectWrapper<Color> bgBaseColor = new ReadOnlyObjectWrapper<>(Color.WHITE);
|
||||||
|
|
||||||
|
public ReadOnlyBooleanProperty contrastCheckerActiveProperty() {
|
||||||
|
return contrastCheckerActive.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
public ColorPalette() {
|
public ColorPalette() {
|
||||||
super();
|
super();
|
||||||
createView();
|
createView();
|
||||||
@ -43,22 +69,22 @@ class ColorPalette extends VBox {
|
|||||||
backBtn.getStyleClass().add(Styles.FLAT);
|
backBtn.getStyleClass().add(Styles.FLAT);
|
||||||
backBtn.setVisible(false);
|
backBtn.setVisible(false);
|
||||||
backBtn.setManaged(false);
|
backBtn.setManaged(false);
|
||||||
backBtn.setOnAction(e -> {
|
backBtn.setOnAction(e -> showColorPalette());
|
||||||
headerLabel.setText("Color Palette");
|
|
||||||
backBtn.setVisible(false);
|
|
||||||
backBtn.setManaged(false);
|
|
||||||
getChildren().set(1, colorGrid);
|
|
||||||
});
|
|
||||||
|
|
||||||
var headerBox = new HBox();
|
var headerBox = new HBox();
|
||||||
headerBox.getChildren().setAll(headerLabel, new Spacer(), backBtn);
|
headerBox.getChildren().setAll(headerLabel, new Spacer(), backBtn);
|
||||||
headerBox.setAlignment(Pos.CENTER_LEFT);
|
headerBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
headerBox.getStyleClass().add("header");
|
||||||
|
|
||||||
contrastCheckerArea = new VBox();
|
contrastCheckerArea = new VBox();
|
||||||
contrastCheckerArea.getStyleClass().add("contrast-checker-area");
|
contrastCheckerArea.getStyleClass().add("contrast-checker-area");
|
||||||
|
|
||||||
colorGrid = colorGrid();
|
colorGrid = colorGrid();
|
||||||
|
|
||||||
|
backgroundProperty().addListener((obs, old, val) -> bgBaseColor.set(
|
||||||
|
val != null && !val.getFills().isEmpty() ? (Color) val.getFills().get(0).getFill() : Color.WHITE
|
||||||
|
));
|
||||||
|
|
||||||
getChildren().setAll(headerBox, colorGrid);
|
getChildren().setAll(headerBox, colorGrid);
|
||||||
setId("color-palette");
|
setId("color-palette");
|
||||||
}
|
}
|
||||||
@ -100,39 +126,34 @@ class ColorPalette extends VBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ColorBlock colorBlock(String fgColor, String bgColor, String borderColor) {
|
private ColorBlock colorBlock(String fgColor, String bgColor, String borderColor) {
|
||||||
var actionHandler = new Consumer<ColorBlock>() {
|
var block = new ColorBlock(fgColor, bgColor, borderColor, bgBaseColor.getReadOnlyProperty());
|
||||||
@Override
|
block.setOnAction(colorBlockActionHandler);
|
||||||
public void accept(ColorBlock colorBlock) {
|
|
||||||
ColorContrastChecker c = getOrCreateContrastChecker();
|
|
||||||
c.setValues(colorBlock.getFgColorName(),
|
|
||||||
colorBlock.getFgColor(),
|
|
||||||
colorBlock.getBgColorName(),
|
|
||||||
colorBlock.getBgColor()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (contrastCheckerArea.getChildren().isEmpty()) {
|
|
||||||
contrastCheckerArea.getChildren().setAll(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
headerLabel.setText("Contrast Checker");
|
|
||||||
backBtn.setVisible(true);
|
|
||||||
backBtn.setManaged(true);
|
|
||||||
getChildren().set(1, contrastCheckerArea);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var block = new ColorBlock(fgColor, bgColor, borderColor);
|
|
||||||
block.setOnAction(actionHandler);
|
|
||||||
blocks.add(block);
|
blocks.add(block);
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ColorContrastChecker getOrCreateContrastChecker() {
|
private ColorContrastChecker getOrCreateContrastChecker() {
|
||||||
if (contrastChecker == null) { contrastChecker = new ColorContrastChecker(); }
|
if (contrastChecker == null) { contrastChecker = new ColorContrastChecker(bgBaseColor.getReadOnlyProperty()); }
|
||||||
VBox.setVgrow(contrastChecker, Priority.ALWAYS);
|
VBox.setVgrow(contrastChecker, Priority.ALWAYS);
|
||||||
return contrastChecker;
|
return contrastChecker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showColorPalette() {
|
||||||
|
headerLabel.setText("Color Palette");
|
||||||
|
backBtn.setVisible(false);
|
||||||
|
backBtn.setManaged(false);
|
||||||
|
getChildren().set(1, colorGrid);
|
||||||
|
contrastCheckerActive.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showContrastChecker() {
|
||||||
|
headerLabel.setText("Contrast Checker");
|
||||||
|
backBtn.setVisible(true);
|
||||||
|
backBtn.setManaged(true);
|
||||||
|
getChildren().set(1, contrastCheckerArea);
|
||||||
|
contrastCheckerActive.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
// To calculate contrast ratio, we have to obtain all components colors first.
|
// To calculate contrast ratio, we have to obtain all components colors first.
|
||||||
// Unfortunately, JavaFX doesn't provide an API to observe when stylesheet changes has been applied.
|
// Unfortunately, JavaFX doesn't provide an API to observe when stylesheet changes has been applied.
|
||||||
// The timer is introduced to defer widget update to a time when scene changes supposedly will be finished.
|
// The timer is introduced to defer widget update to a time when scene changes supposedly will be finished.
|
||||||
|
@ -43,6 +43,7 @@ public class ThemePage extends AbstractPage {
|
|||||||
private GridPane optionsGrid() {
|
private GridPane optionsGrid() {
|
||||||
ChoiceBox<Theme> themeSelector = themeSelector();
|
ChoiceBox<Theme> themeSelector = themeSelector();
|
||||||
themeSelector.setPrefWidth(200);
|
themeSelector.setPrefWidth(200);
|
||||||
|
themeSelector.disableProperty().bind(colorPalette.contrastCheckerActiveProperty());
|
||||||
|
|
||||||
// ~
|
// ~
|
||||||
|
|
||||||
|
@ -101,6 +101,11 @@
|
|||||||
|
|
||||||
#color-palette {
|
#color-palette {
|
||||||
-fx-spacing: 20px;
|
-fx-spacing: 20px;
|
||||||
|
/* mandatory, base bg color for flatten feature */
|
||||||
|
-fx-background-color: -color-bg-default;
|
||||||
|
}
|
||||||
|
#color-palette > .header {
|
||||||
|
-fx-pref-height: 40px;
|
||||||
}
|
}
|
||||||
#color-palette > .grid {
|
#color-palette > .grid {
|
||||||
-fx-vgap: 20px;
|
-fx-vgap: 20px;
|
||||||
@ -150,6 +155,23 @@
|
|||||||
-fx-border-color: -color-contrast-checker-fg;
|
-fx-border-color: -color-contrast-checker-fg;
|
||||||
-fx-border-width: 0 0 1 0;
|
-fx-border-width: 0 0 1 0;
|
||||||
}
|
}
|
||||||
|
#color-palette > .contrast-checker-area > .contrast-checker .button {
|
||||||
|
-color-button-bg: -color-contrast-checker-fg-flat;
|
||||||
|
-color-button-fg: -color-contrast-checker-bg-flat;
|
||||||
|
-color-button-border: -color-contrast-checker-bg-flat;
|
||||||
|
|
||||||
|
-color-button-bg-hover: -color-contrast-checker-fg-flat;
|
||||||
|
-color-button-fg-hover: -color-contrast-checker-bg-flat;
|
||||||
|
-color-button-border-hover: -color-contrast-checker-bg-flat;
|
||||||
|
|
||||||
|
-color-button-bg-focused: -color-contrast-checker-fg-flat;
|
||||||
|
-color-button-fg-focused: -color-contrast-checker-bg-flat;
|
||||||
|
-color-button-border-focused: -color-contrast-checker-bg-flat;
|
||||||
|
|
||||||
|
-color-button-bg-pressed: -color-contrast-checker-bg-flat;
|
||||||
|
-color-button-fg-pressed: -color-contrast-checker-fg-flat;
|
||||||
|
-color-button-border-pressed: -color-contrast-checker-fg-flat;
|
||||||
|
}
|
||||||
#color-palette > .contrast-checker-area > .contrast-checker .ikonli-font-icon {
|
#color-palette > .contrast-checker-area > .contrast-checker .ikonli-font-icon {
|
||||||
-fx-icon-color: -color-contrast-checker-fg;
|
-fx-icon-color: -color-contrast-checker-fg;
|
||||||
-fx-fill: -color-contrast-checker-fg;
|
-fx-fill: -color-contrast-checker-fg;
|
||||||
|
Loading…
Reference in New Issue
Block a user