Display contrast checker in overlay dialog
This commit is contained in:
parent
3ab95551f3
commit
9b0b0bf44c
@ -109,12 +109,6 @@
|
|||||||
<directory>src/main/java/atlantafx/sampler/page</directory>
|
<directory>src/main/java/atlantafx/sampler/page</directory>
|
||||||
<targetPath>atlantafx/sampler/page</targetPath>
|
<targetPath>atlantafx/sampler/page</targetPath>
|
||||||
<filtering>false</filtering>
|
<filtering>false</filtering>
|
||||||
<excludes>
|
|
||||||
<exclude>**/AbstractPage.java</exclude>
|
|
||||||
<exclude>**/CodeViewer.java</exclude>
|
|
||||||
<exclude>**/Page.java</exclude>
|
|
||||||
<exclude>**/SampleBlock.java</exclude>
|
|
||||||
</excludes>
|
|
||||||
</resource>
|
</resource>
|
||||||
<!-- copy icons -->
|
<!-- copy icons -->
|
||||||
<resource>
|
<resource>
|
||||||
|
@ -1,61 +1,14 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
/* SPDX-License-Identifier: MIT */
|
||||||
package atlantafx.sampler.layout;
|
package atlantafx.sampler.layout;
|
||||||
|
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.page.components.OverviewPage;
|
|
||||||
import javafx.animation.FadeTransition;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.util.Duration;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
public class ApplicationWindow extends StackPane {
|
||||||
|
|
||||||
import static javafx.scene.layout.Priority.ALWAYS;
|
|
||||||
|
|
||||||
public class ApplicationWindow extends BorderPane {
|
|
||||||
|
|
||||||
public ApplicationWindow() {
|
public ApplicationWindow() {
|
||||||
var sidebar = new Sidebar();
|
getChildren().setAll(
|
||||||
sidebar.setMinWidth(200);
|
new Overlay(),
|
||||||
|
new MainLayer()
|
||||||
final var pageContainer = new StackPane();
|
);
|
||||||
HBox.setHgrow(pageContainer, ALWAYS);
|
|
||||||
|
|
||||||
sidebar.setOnSelect(pageClass -> {
|
|
||||||
try {
|
|
||||||
final Page prevPage = (!pageContainer.getChildren().isEmpty() && pageContainer.getChildren().get(0) instanceof Page page) ? page : null;
|
|
||||||
final Page nextPage = pageClass.getDeclaredConstructor().newInstance();
|
|
||||||
|
|
||||||
// startup, no animation
|
|
||||||
if (getScene() == null) {
|
|
||||||
pageContainer.getChildren().add(nextPage.getView());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Objects.requireNonNull(prevPage);
|
|
||||||
|
|
||||||
// reset previous page, e.g. to free resources
|
|
||||||
prevPage.reset();
|
|
||||||
|
|
||||||
// animate switching between pages
|
|
||||||
pageContainer.getChildren().add(nextPage.getView());
|
|
||||||
FadeTransition transition = new FadeTransition(Duration.millis(300), nextPage.getView());
|
|
||||||
transition.setFromValue(0.0);
|
|
||||||
transition.setToValue(1.0);
|
|
||||||
transition.setOnFinished(t -> pageContainer.getChildren().remove(prevPage.getView()));
|
|
||||||
transition.play();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ~
|
|
||||||
setLeft(sidebar);
|
|
||||||
setCenter(pageContainer);
|
|
||||||
|
|
||||||
sidebar.select(OverviewPage.class);
|
|
||||||
Platform.runLater(sidebar::requestFocus);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
package atlantafx.sampler.layout;
|
||||||
|
|
||||||
|
import atlantafx.sampler.page.Page;
|
||||||
|
import atlantafx.sampler.page.components.OverviewPage;
|
||||||
|
import javafx.animation.FadeTransition;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static javafx.scene.layout.Priority.ALWAYS;
|
||||||
|
|
||||||
|
class MainLayer extends BorderPane {
|
||||||
|
|
||||||
|
public MainLayer() {
|
||||||
|
super();
|
||||||
|
createView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createView() {
|
||||||
|
var sidebar = new Sidebar();
|
||||||
|
sidebar.setMinWidth(200);
|
||||||
|
|
||||||
|
final var pageContainer = new StackPane();
|
||||||
|
HBox.setHgrow(pageContainer, ALWAYS);
|
||||||
|
|
||||||
|
sidebar.setOnSelect(pageClass -> {
|
||||||
|
try {
|
||||||
|
final Page prevPage = (!pageContainer.getChildren().isEmpty() && pageContainer.getChildren().get(0) instanceof Page page) ? page : null;
|
||||||
|
final Page nextPage = pageClass.getDeclaredConstructor().newInstance();
|
||||||
|
|
||||||
|
// startup, no animation
|
||||||
|
if (getScene() == null) {
|
||||||
|
pageContainer.getChildren().add(nextPage.getView());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Objects.requireNonNull(prevPage);
|
||||||
|
|
||||||
|
// reset previous page, e.g. to free resources
|
||||||
|
prevPage.reset();
|
||||||
|
|
||||||
|
// animate switching between pages
|
||||||
|
pageContainer.getChildren().add(nextPage.getView());
|
||||||
|
FadeTransition transition = new FadeTransition(Duration.millis(300), nextPage.getView());
|
||||||
|
transition.setFromValue(0.0);
|
||||||
|
transition.setToValue(1.0);
|
||||||
|
transition.setOnFinished(t -> pageContainer.getChildren().remove(prevPage.getView()));
|
||||||
|
transition.play();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ~
|
||||||
|
setLeft(sidebar);
|
||||||
|
setCenter(pageContainer);
|
||||||
|
|
||||||
|
sidebar.select(OverviewPage.class);
|
||||||
|
Platform.runLater(sidebar::requestFocus);
|
||||||
|
}
|
||||||
|
}
|
156
sampler/src/main/java/atlantafx/sampler/layout/Overlay.java
Normal file
156
sampler/src/main/java/atlantafx/sampler/layout/Overlay.java
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
package atlantafx.sampler.layout;
|
||||||
|
|
||||||
|
import atlantafx.sampler.util.Animations;
|
||||||
|
import atlantafx.sampler.util.Containers;
|
||||||
|
import atlantafx.sampler.util.NodeUtils;
|
||||||
|
import javafx.animation.Timeline;
|
||||||
|
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||||
|
import javafx.event.Event;
|
||||||
|
import javafx.geometry.HPos;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyEvent;
|
||||||
|
import javafx.scene.input.MouseButton;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class Overlay extends StackPane {
|
||||||
|
|
||||||
|
public static final String STYLE_CLASS = "overlay";
|
||||||
|
|
||||||
|
private ScrollPane scrollPane;
|
||||||
|
private AnchorPane edgeContentWrapper;
|
||||||
|
private StackPane centerContentWrapper;
|
||||||
|
|
||||||
|
private final ReadOnlyBooleanWrapper onFrontProperty = new ReadOnlyBooleanWrapper(false);
|
||||||
|
private final Timeline fadeInTransition = Animations.fadeIn(this, Duration.millis(100));
|
||||||
|
private final Timeline fadeOutTransition = Animations.fadeOut(this, Duration.millis(200));
|
||||||
|
|
||||||
|
private HPos currentContentPos;
|
||||||
|
|
||||||
|
public Overlay() {
|
||||||
|
createView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createView() {
|
||||||
|
edgeContentWrapper = new AnchorPane();
|
||||||
|
edgeContentWrapper.getStyleClass().add("scrollable-content");
|
||||||
|
|
||||||
|
centerContentWrapper = new StackPane();
|
||||||
|
centerContentWrapper.getStyleClass().add("scrollable-content");
|
||||||
|
centerContentWrapper.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
scrollPane = new ScrollPane();
|
||||||
|
Containers.setScrollConstraints(scrollPane,
|
||||||
|
ScrollPane.ScrollBarPolicy.AS_NEEDED, true,
|
||||||
|
ScrollPane.ScrollBarPolicy.NEVER, true
|
||||||
|
);
|
||||||
|
scrollPane.setMaxHeight(10_000); // scroll pane won't work without height specified
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
Consumer<Event> hideAndConsume = e -> {
|
||||||
|
removeContent();
|
||||||
|
toBack();
|
||||||
|
e.consume();
|
||||||
|
};
|
||||||
|
|
||||||
|
// hide overlay by pressing ESC (only works when overlay or one of its children has focus,
|
||||||
|
// that's why we requesting it in the toFront())
|
||||||
|
addEventHandler(KeyEvent.KEY_PRESSED, e -> {
|
||||||
|
if (e.getCode() == KeyCode.ESCAPE) { hideAndConsume.accept(e); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// hide overlay by clicking outside content area
|
||||||
|
setOnMouseClicked(e -> {
|
||||||
|
Pane content = getContent();
|
||||||
|
Node eventSource = e.getPickResult().getIntersectedNode();
|
||||||
|
if (e.getButton() == MouseButton.PRIMARY && content != null && !NodeUtils.isDescendant(content, eventSource)) {
|
||||||
|
hideAndConsume.accept(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
getChildren().add(scrollPane);
|
||||||
|
getStyleClass().add(STYLE_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pane getContent() {
|
||||||
|
return NodeUtils.getChildByIndex(getContentWrapper(), 0, Pane.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pane getContentWrapper() {
|
||||||
|
return currentContentPos == HPos.CENTER ? centerContentWrapper : edgeContentWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(Pane content, HPos pos) {
|
||||||
|
Objects.requireNonNull(content);
|
||||||
|
Objects.requireNonNull(pos);
|
||||||
|
|
||||||
|
// clear old content
|
||||||
|
if (pos != currentContentPos) {
|
||||||
|
removeContent();
|
||||||
|
currentContentPos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pos) {
|
||||||
|
case LEFT -> {
|
||||||
|
edgeContentWrapper.getChildren().setAll(content);
|
||||||
|
Containers.setAnchors(content, new Insets(0, -1, 0, 0));
|
||||||
|
}
|
||||||
|
case RIGHT -> {
|
||||||
|
edgeContentWrapper.getChildren().setAll(content);
|
||||||
|
Containers.setAnchors(content, new Insets(0, 0, 0, -1));
|
||||||
|
}
|
||||||
|
case CENTER -> {
|
||||||
|
centerContentWrapper.getChildren().setAll(content);
|
||||||
|
StackPane.setAlignment(content, Pos.CENTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollPane.setContent(getContentWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeContent() {
|
||||||
|
getContentWrapper().getChildren().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Pane content) {
|
||||||
|
return content != null &&
|
||||||
|
getContentWrapper().getChildren().size() > 0 &&
|
||||||
|
getContentWrapper().getChildren().get(0).equals(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toFront() {
|
||||||
|
if (onFrontProperty.get()) { return; }
|
||||||
|
super.toFront();
|
||||||
|
fadeInTransition.playFromStart();
|
||||||
|
onFrontProperty.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toBack() {
|
||||||
|
if (!onFrontProperty.get()) { return; }
|
||||||
|
super.toBack();
|
||||||
|
fadeOutTransition.playFromStart();
|
||||||
|
onFrontProperty.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyBooleanProperty onFrontProperty() {
|
||||||
|
return onFrontProperty.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnFront() {
|
||||||
|
return onFrontProperty().get();
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,7 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
import static javafx.scene.layout.Priority.ALWAYS;
|
import static javafx.scene.layout.Priority.ALWAYS;
|
||||||
|
|
||||||
public class Sidebar extends VBox {
|
class Sidebar extends VBox {
|
||||||
|
|
||||||
private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
|
private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ package atlantafx.sampler.page;
|
|||||||
import atlantafx.base.controls.Popover;
|
import atlantafx.base.controls.Popover;
|
||||||
import atlantafx.base.controls.Spacer;
|
import atlantafx.base.controls.Spacer;
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.sampler.layout.Overlay;
|
||||||
import atlantafx.sampler.theme.ThemeManager;
|
import atlantafx.sampler.theme.ThemeManager;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
@ -48,6 +49,7 @@ public abstract class AbstractPage extends BorderPane implements Page {
|
|||||||
protected StackPane codeViewerWrapper;
|
protected StackPane codeViewerWrapper;
|
||||||
protected CodeViewer codeViewer;
|
protected CodeViewer codeViewer;
|
||||||
protected VBox userContent;
|
protected VBox userContent;
|
||||||
|
protected Overlay overlay;
|
||||||
protected boolean isRendered = false;
|
protected boolean isRendered = false;
|
||||||
|
|
||||||
protected AbstractPage() {
|
protected AbstractPage() {
|
||||||
@ -129,9 +131,16 @@ public abstract class AbstractPage extends BorderPane implements Page {
|
|||||||
|
|
||||||
// Some properties can only be obtained after node placed
|
// Some properties can only be obtained after node placed
|
||||||
// to the scene graph and here is the place do this.
|
// to the scene graph and here is the place do this.
|
||||||
protected void onRendered() { }
|
protected void onRendered() {
|
||||||
|
this.overlay = lookupOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
private void showThemeConfigPopover() {
|
protected Overlay lookupOverlay() {
|
||||||
|
return getScene() != null && getScene().lookup("." + Overlay.STYLE_CLASS) instanceof Overlay overlay ?
|
||||||
|
overlay : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showThemeConfigPopover() {
|
||||||
if (quickConfigPopover == null) {
|
if (quickConfigPopover == null) {
|
||||||
var content = new QuickConfigMenu();
|
var content = new QuickConfigMenu();
|
||||||
content.setExitHandler(() -> quickConfigPopover.hide());
|
content.setExitHandler(() -> quickConfigPopover.hide());
|
||||||
|
113
sampler/src/main/java/atlantafx/sampler/page/OverlayDialog.java
Normal file
113
sampler/src/main/java/atlantafx/sampler/page/OverlayDialog.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
package atlantafx.sampler.page;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.Spacer;
|
||||||
|
import atlantafx.sampler.util.Containers;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
import org.kordamp.ikonli.material2.Material2AL;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static atlantafx.base.theme.Styles.*;
|
||||||
|
|
||||||
|
public abstract class OverlayDialog<T extends Region> extends VBox {
|
||||||
|
|
||||||
|
protected static final int CONTENT_CHILD_INDEX = 1;
|
||||||
|
|
||||||
|
protected Label titleLabel;
|
||||||
|
protected Button topCloseBtn;
|
||||||
|
protected HBox headerBox;
|
||||||
|
|
||||||
|
protected Button bottomCloseBtn;
|
||||||
|
protected HBox footerBox;
|
||||||
|
|
||||||
|
protected Runnable onCloseCallback;
|
||||||
|
|
||||||
|
public OverlayDialog() {
|
||||||
|
createView();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createView() {
|
||||||
|
titleLabel = new Label();
|
||||||
|
titleLabel.getStyleClass().addAll(TITLE_4, "title");
|
||||||
|
|
||||||
|
topCloseBtn = new Button("", new FontIcon(Material2AL.CLOSE));
|
||||||
|
topCloseBtn.getStyleClass().addAll(BUTTON_ICON, BUTTON_CIRCLE, FLAT, "close-button");
|
||||||
|
topCloseBtn.setOnAction(e -> close());
|
||||||
|
|
||||||
|
headerBox = new HBox(10);
|
||||||
|
headerBox.getStyleClass().add("header");
|
||||||
|
headerBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
headerBox.getChildren().setAll(
|
||||||
|
titleLabel,
|
||||||
|
new Spacer(),
|
||||||
|
topCloseBtn
|
||||||
|
);
|
||||||
|
VBox.setVgrow(headerBox, Priority.NEVER);
|
||||||
|
|
||||||
|
bottomCloseBtn = new Button("Close");
|
||||||
|
bottomCloseBtn.getStyleClass().add("form-action");
|
||||||
|
bottomCloseBtn.setOnAction(e -> close());
|
||||||
|
bottomCloseBtn.setCancelButton(true);
|
||||||
|
|
||||||
|
footerBox = new HBox(10);
|
||||||
|
footerBox.getStyleClass().add("footer");
|
||||||
|
footerBox.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
footerBox.getChildren().setAll(
|
||||||
|
new Spacer(),
|
||||||
|
bottomCloseBtn
|
||||||
|
);
|
||||||
|
VBox.setVgrow(footerBox, Priority.NEVER);
|
||||||
|
|
||||||
|
// IMPORTANT: this guarantees client will use correct width and height
|
||||||
|
Containers.usePrefWidth(this);
|
||||||
|
Containers.usePrefHeight(this);
|
||||||
|
|
||||||
|
// if you're updating this line, update setContent() method as well
|
||||||
|
getChildren().setAll(headerBox, footerBox);
|
||||||
|
|
||||||
|
getStyleClass().add("overlay-dialog");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setContent(T content) {
|
||||||
|
Objects.requireNonNull(content);
|
||||||
|
VBox.setVgrow(content, Priority.ALWAYS);
|
||||||
|
|
||||||
|
// content have to be placed exactly between header and footer
|
||||||
|
if (getChildren().size() == 2) {
|
||||||
|
// add new content
|
||||||
|
getChildren().add(CONTENT_CHILD_INDEX, content);
|
||||||
|
} else if (getChildren().size() == 3) {
|
||||||
|
// overwrite existing content
|
||||||
|
getChildren().set(CONTENT_CHILD_INDEX, content);
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Content cannot be placed because of unexpected children size. " +
|
||||||
|
"You should override 'OverlayDialog#setContent()' and place it manually.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setTitle(String title) {
|
||||||
|
titleLabel.setText(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if (onCloseCallback != null) {
|
||||||
|
onCloseCallback.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Runnable getOnCloseRequest() {
|
||||||
|
return onCloseCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnCloseRequest(Runnable handler) {
|
||||||
|
this.onCloseCallback = handler;
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,7 @@ import org.kordamp.ikonli.material2.Material2AL;
|
|||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static atlantafx.sampler.page.general.ColorContrastChecker.*;
|
import static atlantafx.sampler.page.general.ContrastChecker.*;
|
||||||
import static atlantafx.sampler.util.JColorUtils.flattenColor;
|
import static atlantafx.sampler.util.JColorUtils.flattenColor;
|
||||||
import static atlantafx.sampler.util.JColorUtils.getColorLuminance;
|
import static atlantafx.sampler.util.JColorUtils.getColorLuminance;
|
||||||
|
|
||||||
@ -71,8 +71,8 @@ class ColorBlock extends VBox {
|
|||||||
if (bgFill == null) { return; }
|
if (bgFill == null) { return; }
|
||||||
|
|
||||||
toggleHover(true);
|
toggleHover(true);
|
||||||
expandIcon.setFill(
|
expandIcon.setFill(getColorLuminance(flattenColor(bgBaseColor.get(), bgFill)) < LUMINANCE_THRESHOLD ?
|
||||||
getColorLuminance(flattenColor(bgBaseColor.get(), bgFill)) < LUMINANCE_THRESHOLD ? Color.WHITE : Color.BLACK
|
Color.WHITE : Color.BLACK
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
colorBox.setOnMouseExited(e -> toggleHover(false));
|
colorBox.setOnMouseExited(e -> toggleHover(false));
|
||||||
|
@ -4,82 +4,42 @@ 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.ReadOnlyObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
|
||||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
class ColorPalette extends VBox {
|
class ColorPalette extends VBox {
|
||||||
|
|
||||||
private Label headerLabel;
|
|
||||||
private Button backBtn;
|
|
||||||
private GridPane colorGrid;
|
|
||||||
private ColorContrastChecker contrastChecker;
|
|
||||||
private VBox contrastCheckerArea;
|
|
||||||
|
|
||||||
private final List<ColorBlock> blocks = new ArrayList<>();
|
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);
|
private final ReadOnlyObjectWrapper<Color> bgBaseColor = new ReadOnlyObjectWrapper<>(Color.WHITE);
|
||||||
|
private final Consumer<ColorBlock> colorBlockActionHandler;
|
||||||
|
|
||||||
public ReadOnlyBooleanProperty contrastCheckerActiveProperty() {
|
public ColorPalette(Consumer<ColorBlock> blockClickedHandler) {
|
||||||
return contrastCheckerActive.getReadOnlyProperty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ColorPalette() {
|
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this.colorBlockActionHandler = Objects.requireNonNull(blockClickedHandler);
|
||||||
createView();
|
createView();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createView() {
|
private void createView() {
|
||||||
headerLabel = new Label("Color Palette");
|
var headerLabel = new Label("Color Palette");
|
||||||
headerLabel.getStyleClass().add(Styles.TITLE_4);
|
headerLabel.getStyleClass().add(Styles.TITLE_4);
|
||||||
|
|
||||||
backBtn = new Button("Back", new FontIcon(Feather.CHEVRONS_LEFT));
|
|
||||||
backBtn.getStyleClass().add(Styles.FLAT);
|
|
||||||
backBtn.setVisible(false);
|
|
||||||
backBtn.setManaged(false);
|
|
||||||
backBtn.setOnAction(e -> showColorPalette());
|
|
||||||
|
|
||||||
var headerBox = new HBox();
|
var headerBox = new HBox();
|
||||||
headerBox.getChildren().setAll(headerLabel, new Spacer(), backBtn);
|
headerBox.getChildren().setAll(headerLabel, new Spacer());
|
||||||
headerBox.setAlignment(Pos.CENTER_LEFT);
|
headerBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
headerBox.getStyleClass().add("header");
|
headerBox.getStyleClass().add("header");
|
||||||
|
|
||||||
contrastCheckerArea = new VBox();
|
var colorGrid = colorGrid();
|
||||||
contrastCheckerArea.getStyleClass().add("contrast-checker-area");
|
|
||||||
|
|
||||||
colorGrid = colorGrid();
|
|
||||||
|
|
||||||
backgroundProperty().addListener((obs, old, val) -> bgBaseColor.set(
|
backgroundProperty().addListener((obs, old, val) -> bgBaseColor.set(
|
||||||
val != null && !val.getFills().isEmpty() ? (Color) val.getFills().get(0).getFill() : Color.WHITE
|
val != null && !val.getFills().isEmpty() ? (Color) val.getFills().get(0).getFill() : Color.WHITE
|
||||||
@ -132,28 +92,6 @@ class ColorPalette extends VBox {
|
|||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ColorContrastChecker getOrCreateContrastChecker() {
|
|
||||||
if (contrastChecker == null) { contrastChecker = new ColorContrastChecker(bgBaseColor.getReadOnlyProperty()); }
|
|
||||||
VBox.setVgrow(contrastChecker, Priority.ALWAYS);
|
|
||||||
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.
|
||||||
@ -165,4 +103,8 @@ class ColorPalette extends VBox {
|
|||||||
}
|
}
|
||||||
}, delay.toMillis());
|
}, delay.toMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<Color> bgBaseColorProperty() {
|
||||||
|
return bgBaseColor.getReadOnlyProperty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
package atlantafx.sampler.page.general;
|
package atlantafx.sampler.page.general;
|
||||||
|
|
||||||
import atlantafx.base.controls.CustomTextField;
|
import atlantafx.base.controls.CustomTextField;
|
||||||
|
import atlantafx.base.controls.Spacer;
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.sampler.theme.ThemeManager;
|
import atlantafx.sampler.theme.ThemeManager;
|
||||||
import atlantafx.sampler.util.JColor;
|
import atlantafx.sampler.util.JColor;
|
||||||
@ -33,16 +34,15 @@ import org.kordamp.ikonli.material2.Material2AL;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static atlantafx.sampler.page.general.ColorBlock.validateColorName;
|
import static atlantafx.sampler.page.general.ColorBlock.validateColorName;
|
||||||
import static atlantafx.sampler.util.JColorUtils.*;
|
import static atlantafx.sampler.util.JColorUtils.flattenColor;
|
||||||
|
import static atlantafx.sampler.util.JColorUtils.getColorLuminance;
|
||||||
|
|
||||||
// Inspired by the https://colourcontrast.cc/
|
// Inspired by the https://colourcontrast.cc/
|
||||||
public class ColorContrastChecker extends GridPane {
|
public class ContrastChecker extends GridPane {
|
||||||
|
|
||||||
static final PseudoClass PASSED = PseudoClass.getPseudoClass("passed");
|
public static final double CONTRAST_RATIO_THRESHOLD = 1.5;
|
||||||
static final float[] COLOR_WHITE = new float[] { 255f, 255f, 255f, 1f };
|
public static final double LUMINANCE_THRESHOLD = 0.55;
|
||||||
static final float[] COLOR_BLACK = new float[] { 0f, 0f, 0f, 1f };
|
public static final PseudoClass PASSED = PseudoClass.getPseudoClass("passed");
|
||||||
static final double CONTRAST_RATIO_THRESHOLD = 1.5;
|
|
||||||
static final double LUMINANCE_THRESHOLD = 0.55;
|
|
||||||
|
|
||||||
private static final int SLIDER_WIDTH = 300;
|
private static final int SLIDER_WIDTH = 300;
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
private Slider fgLightnessSlider;
|
private Slider fgLightnessSlider;
|
||||||
private Slider fgAlphaSlider;
|
private Slider fgAlphaSlider;
|
||||||
|
|
||||||
public ColorContrastChecker(ReadOnlyObjectProperty<Color> bgBaseColor) {
|
public ContrastChecker(ReadOnlyObjectProperty<Color> bgBaseColor) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.bgBaseColor = bgBaseColor;
|
this.bgBaseColor = bgBaseColor;
|
||||||
@ -98,6 +98,19 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
|
|
||||||
public Color getFgColor() { return fgColor.colorProperty().get(); }
|
public Color getFgColor() { return fgColor.colorProperty().get(); }
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<Color> bgColorProperty() { return bgColor.colorProperty(); }
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<Color> fgColorProperty() { return fgColor.colorProperty(); }
|
||||||
|
|
||||||
|
// Returns fg color that guaranteed to be visible on the current bg.
|
||||||
|
public Color getSafeFgColor() {
|
||||||
|
if (contrastRatio.get() <= CONTRAST_RATIO_THRESHOLD) {
|
||||||
|
return getColorLuminance(flattenColor(bgBaseColor.get(), bgColor.getColor())) < LUMINANCE_THRESHOLD ? Color.WHITE : Color.BLACK;
|
||||||
|
} else {
|
||||||
|
return fgColor.getColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createView() {
|
private void createView() {
|
||||||
var textLabel = new Label("Aa");
|
var textLabel = new Label("Aa");
|
||||||
textLabel.getStyleClass().add("text");
|
textLabel.getStyleClass().add("text");
|
||||||
@ -274,15 +287,16 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
tm.reloadCustomCSS();
|
tm.reloadCustomCSS();
|
||||||
});
|
});
|
||||||
|
|
||||||
var controlsBox = new HBox(20, flattenBtn, applyBtn);
|
var controlsBox = new HBox(20, new Spacer(), flattenBtn, applyBtn);
|
||||||
controlsBox.setAlignment(Pos.CENTER_LEFT);
|
controlsBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
controlsBox.setPadding(new Insets(10, 0, 0, 0));
|
||||||
|
|
||||||
// ~
|
// ~
|
||||||
|
|
||||||
getStyleClass().add("contrast-checker");
|
getStyleClass().add("contrast-checker");
|
||||||
|
|
||||||
// column 0
|
// column 0
|
||||||
add(fontBox, 0, 0);
|
add(new HBox(fontBox, new Spacer(), wsagBox), 0, 0, REMAINING, 1);
|
||||||
add(new Label("Background Color"), 0, 1);
|
add(new Label("Background Color"), 0, 1);
|
||||||
add(bgColorNameLabel, 0, 2);
|
add(bgColorNameLabel, 0, 2);
|
||||||
add(bgTextField, 0, 3);
|
add(bgTextField, 0, 3);
|
||||||
@ -298,7 +312,6 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
add(controlsBox, 0, 12, REMAINING, 1);
|
add(controlsBox, 0, 12, REMAINING, 1);
|
||||||
|
|
||||||
// column 1
|
// column 1
|
||||||
add(wsagBox, 1, 0);
|
|
||||||
add(new Label("Foreground Color"), 1, 1);
|
add(new Label("Foreground Color"), 1, 1);
|
||||||
add(fgColorNameLabel, 1, 2);
|
add(fgColorNameLabel, 1, 2);
|
||||||
add(fgTextField, 1, 3);
|
add(fgTextField, 1, 3);
|
||||||
@ -321,40 +334,14 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateStyle() {
|
private void updateStyle() {
|
||||||
float[] bg = bgColor.getRGBAColor();
|
setStyle(String.format("-color-contrast-checker-bg:%s;-color-contrast-checker-fg:%s;",
|
||||||
float[] fg = fgColor.getRGBAColor();
|
JColorUtils.toHexWithAlpha(bgColor.getColor()),
|
||||||
|
JColorUtils.toHexWithAlpha(getSafeFgColor())
|
||||||
// use fallback color if contrast ratio is too low
|
));
|
||||||
if (contrastRatio.get() <= CONTRAST_RATIO_THRESHOLD) {
|
|
||||||
fg = getColorLuminance(flattenColor(bgBaseColor.get(), bgColor.getColor())) < LUMINANCE_THRESHOLD ?
|
|
||||||
COLOR_WHITE :
|
|
||||||
COLOR_BLACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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],
|
|
||||||
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) {
|
||||||
float[] hsl = JColorUtils.toHSL(
|
float[] hsl = JColorUtils.toHSL(color);
|
||||||
(float) color.getRed(),
|
|
||||||
(float) color.getGreen(),
|
|
||||||
(float) color.getBlue()
|
|
||||||
);
|
|
||||||
bgHueSlider.setValue(hsl[0]);
|
bgHueSlider.setValue(hsl[0]);
|
||||||
bgSaturationSlider.setValue(hsl[1]);
|
bgSaturationSlider.setValue(hsl[1]);
|
||||||
bgLightnessSlider.setValue(hsl[2]);
|
bgLightnessSlider.setValue(hsl[2]);
|
||||||
@ -362,11 +349,7 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setForeground(Color color) {
|
private void setForeground(Color color) {
|
||||||
float[] hsl = JColorUtils.toHSL(
|
float[] hsl = JColorUtils.toHSL(color);
|
||||||
(float) color.getRed(),
|
|
||||||
(float) color.getGreen(),
|
|
||||||
(float) color.getBlue()
|
|
||||||
);
|
|
||||||
fgHueSlider.setValue(hsl[0]);
|
fgHueSlider.setValue(hsl[0]);
|
||||||
fgSaturationSlider.setValue(hsl[1]);
|
fgSaturationSlider.setValue(hsl[1]);
|
||||||
fgLightnessSlider.setValue(hsl[2]);
|
fgLightnessSlider.setValue(hsl[2]);
|
||||||
@ -412,7 +395,7 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
static double getContrastRatioOpacityAware(Color bgColor, Color fgColor, Color bgBaseColor) {
|
static double getContrastRatioOpacityAware(Color bgColor, Color fgColor, Color bgBaseColor) {
|
||||||
double luminance1 = getColorLuminance(flattenColor(bgBaseColor, bgColor));
|
double luminance1 = getColorLuminance(flattenColor(bgBaseColor, bgColor));
|
||||||
double luminance2 = getColorLuminance(flattenColor(bgBaseColor, fgColor));
|
double luminance2 = getColorLuminance(flattenColor(bgBaseColor, fgColor));
|
||||||
return getContrastRatio(luminance1, luminance2);
|
return JColorUtils.getContrastRatio(luminance1, luminance2);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@ -435,11 +418,7 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setColor(Color color) {
|
public void setColor(Color color) {
|
||||||
float[] hsl = JColorUtils.toHSL(
|
float[] hsl = JColorUtils.toHSL(color);
|
||||||
(float) color.getRed(),
|
|
||||||
(float) color.getGreen(),
|
|
||||||
(float) color.getBlue()
|
|
||||||
);
|
|
||||||
values.setAll(hsl[0], hsl[1], hsl[2], (float) color.getOpacity());
|
values.setAll(hsl[0], hsl[1], hsl[2], (float) color.getOpacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,17 +469,6 @@ public class ColorContrastChecker extends GridPane {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public float[] getRGBAColor() {
|
|
||||||
float[] hsl = new float[] { getHue(), getSaturation(), getLightness() };
|
|
||||||
var color = JColor.color(hsl, getAlpha());
|
|
||||||
return new float[] {
|
|
||||||
color.getRed(),
|
|
||||||
color.getGreen(),
|
|
||||||
color.getBlue(),
|
|
||||||
getAlpha()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getColorHexWithAlpha() {
|
public String getColorHexWithAlpha() {
|
||||||
float[] hsl = new float[] { getHue(), getSaturation(), getLightness() };
|
float[] hsl = new float[] { getHue(), getSaturation(), getLightness() };
|
||||||
return JColor.color(hsl, getAlpha()).getColorHexWithAlpha();
|
return JColor.color(hsl, getAlpha()).getColorHexWithAlpha();
|
@ -0,0 +1,36 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
package atlantafx.sampler.page.general;
|
||||||
|
|
||||||
|
import atlantafx.sampler.page.OverlayDialog;
|
||||||
|
import atlantafx.sampler.util.JColorUtils;
|
||||||
|
import atlantafx.sampler.util.NodeUtils;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
|
class ContrastCheckerDialog extends OverlayDialog<ContrastChecker> {
|
||||||
|
|
||||||
|
private final ContrastChecker contrastChecker;
|
||||||
|
|
||||||
|
public ContrastCheckerDialog(ReadOnlyObjectProperty<Color> bgBaseColor) {
|
||||||
|
this.contrastChecker = new ContrastChecker(bgBaseColor);
|
||||||
|
|
||||||
|
contrastChecker.bgColorProperty().addListener((obs, old, val) -> updateStyle());
|
||||||
|
contrastChecker.fgColorProperty().addListener((obs, old, val) -> updateStyle());
|
||||||
|
|
||||||
|
getStyleClass().add("contrast-checker-dialog");
|
||||||
|
setTitle("Contrast Checker");
|
||||||
|
setContent(contrastChecker);
|
||||||
|
NodeUtils.toggleVisibility(footerBox, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStyle() {
|
||||||
|
setStyle(String.format("-color-dialog-bg:%s;-color-dialog-fg:%s;",
|
||||||
|
JColorUtils.toHexWithAlpha(contrastChecker.getBgColor()),
|
||||||
|
JColorUtils.toHexWithAlpha(contrastChecker.getSafeFgColor())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContrastChecker getContent() {
|
||||||
|
return contrastChecker;
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,8 @@ import atlantafx.base.theme.Theme;
|
|||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.AbstractPage;
|
||||||
import atlantafx.sampler.theme.ThemeEvent.EventType;
|
import atlantafx.sampler.theme.ThemeEvent.EventType;
|
||||||
import atlantafx.sampler.theme.ThemeManager;
|
import atlantafx.sampler.theme.ThemeManager;
|
||||||
|
import atlantafx.sampler.util.NodeUtils;
|
||||||
|
import javafx.geometry.HPos;
|
||||||
import javafx.scene.control.ChoiceBox;
|
import javafx.scene.control.ChoiceBox;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
@ -12,12 +14,26 @@ import javafx.util.StringConverter;
|
|||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class ThemePage extends AbstractPage {
|
public class ThemePage extends AbstractPage {
|
||||||
|
|
||||||
public static final String NAME = "Theme";
|
public static final String NAME = "Theme";
|
||||||
|
|
||||||
private final ColorPalette colorPalette = new ColorPalette();
|
private final Consumer<ColorBlock> colorBlockActionHandler = colorBlock -> {
|
||||||
|
ContrastCheckerDialog dialog = getOrCreateContrastCheckerDialog();
|
||||||
|
dialog.getContent().setValues(colorBlock.getFgColorName(),
|
||||||
|
colorBlock.getFgColor(),
|
||||||
|
colorBlock.getBgColorName(),
|
||||||
|
colorBlock.getBgColor()
|
||||||
|
);
|
||||||
|
overlay.setContent(dialog, HPos.CENTER);
|
||||||
|
overlay.toFront();
|
||||||
|
};
|
||||||
|
|
||||||
|
private final ColorPalette colorPalette = new ColorPalette(colorBlockActionHandler);
|
||||||
|
|
||||||
|
private ContrastCheckerDialog contrastCheckerDialog;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() { return NAME; }
|
public String getName() { return NAME; }
|
||||||
@ -46,16 +62,13 @@ public class ThemePage extends AbstractPage {
|
|||||||
);
|
);
|
||||||
// if you want to enable quick menu don't forget that
|
// if you want to enable quick menu don't forget that
|
||||||
// theme selection choice box have to be updated accordingly
|
// theme selection choice box have to be updated accordingly
|
||||||
quickConfigBtn.setVisible(false);
|
NodeUtils.toggleVisibility(quickConfigBtn, false);
|
||||||
quickConfigBtn.setManaged(false);
|
NodeUtils.toggleVisibility(sourceCodeToggleBtn, false);
|
||||||
sourceCodeToggleBtn.setVisible(false);
|
|
||||||
sourceCodeToggleBtn.setManaged(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
// ~
|
// ~
|
||||||
|
|
||||||
@ -106,4 +119,17 @@ public class ThemePage extends AbstractPage {
|
|||||||
|
|
||||||
return selector;
|
return selector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ContrastCheckerDialog getOrCreateContrastCheckerDialog() {
|
||||||
|
if (contrastCheckerDialog == null) {
|
||||||
|
contrastCheckerDialog = new ContrastCheckerDialog(colorPalette.bgBaseColorProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
contrastCheckerDialog.setOnCloseRequest(() -> {
|
||||||
|
overlay.removeContent();
|
||||||
|
overlay.toBack();
|
||||||
|
});
|
||||||
|
|
||||||
|
return contrastCheckerDialog;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import atlantafx.sampler.page.AbstractPage;
|
|||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.SampleBlock;
|
||||||
import atlantafx.sampler.theme.ThemeEvent.EventType;
|
import atlantafx.sampler.theme.ThemeEvent.EventType;
|
||||||
import atlantafx.sampler.theme.ThemeManager;
|
import atlantafx.sampler.theme.ThemeManager;
|
||||||
|
import atlantafx.sampler.util.NodeUtils;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
@ -74,10 +75,8 @@ public class TypographyPage extends AbstractPage {
|
|||||||
);
|
);
|
||||||
// if you want to enable quick menu don't forget that
|
// if you want to enable quick menu don't forget that
|
||||||
// font size spinner value have to be updated accordingly
|
// font size spinner value have to be updated accordingly
|
||||||
quickConfigBtn.setVisible(false);
|
NodeUtils.toggleVisibility(quickConfigBtn, false);
|
||||||
quickConfigBtn.setManaged(false);
|
NodeUtils.toggleVisibility(sourceCodeToggleBtn, false);
|
||||||
sourceCodeToggleBtn.setVisible(false);
|
|
||||||
sourceCodeToggleBtn.setManaged(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ComboBox<String> fontFamilyChooser() {
|
private ComboBox<String> fontFamilyChooser() {
|
||||||
|
63
sampler/src/main/java/atlantafx/sampler/util/Animations.java
Normal file
63
sampler/src/main/java/atlantafx/sampler/util/Animations.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
package atlantafx.sampler.util;
|
||||||
|
|
||||||
|
import javafx.animation.Interpolator;
|
||||||
|
import javafx.animation.KeyFrame;
|
||||||
|
import javafx.animation.KeyValue;
|
||||||
|
import javafx.animation.Timeline;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
public final class Animations {
|
||||||
|
|
||||||
|
public static final Interpolator EASE = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
|
||||||
|
|
||||||
|
public static Timeline fadeIn(Node node, Duration duration) {
|
||||||
|
return new Timeline(
|
||||||
|
new KeyFrame(Duration.millis(0), new KeyValue(node.opacityProperty(), 0, EASE)),
|
||||||
|
new KeyFrame(duration, new KeyValue(node.opacityProperty(), 1, EASE))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Timeline fadeOut(Node node, Duration duration) {
|
||||||
|
return new Timeline(
|
||||||
|
new KeyFrame(Duration.millis(0), new KeyValue(node.opacityProperty(), 1, EASE)),
|
||||||
|
new KeyFrame(duration, new KeyValue(node.opacityProperty(), 0, EASE))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Timeline zoomIn(Node node, Duration duration) {
|
||||||
|
return new Timeline(
|
||||||
|
new KeyFrame(Duration.millis(0),
|
||||||
|
new KeyValue(node.opacityProperty(), 0, EASE),
|
||||||
|
new KeyValue(node.scaleXProperty(), 0.3, EASE),
|
||||||
|
new KeyValue(node.scaleYProperty(), 0.3, EASE),
|
||||||
|
new KeyValue(node.scaleZProperty(), 0.3, EASE)
|
||||||
|
),
|
||||||
|
new KeyFrame(duration,
|
||||||
|
new KeyValue(node.opacityProperty(), 1, EASE),
|
||||||
|
new KeyValue(node.scaleXProperty(), 1, EASE),
|
||||||
|
new KeyValue(node.scaleYProperty(), 1, EASE),
|
||||||
|
new KeyValue(node.scaleZProperty(), 1, EASE)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Timeline zoomOut(Node node, Duration duration) {
|
||||||
|
return new Timeline(
|
||||||
|
new KeyFrame(Duration.millis(0),
|
||||||
|
new KeyValue(node.opacityProperty(), 1, EASE),
|
||||||
|
new KeyValue(node.scaleXProperty(), 1, EASE),
|
||||||
|
new KeyValue(node.scaleYProperty(), 1, EASE),
|
||||||
|
new KeyValue(node.scaleZProperty(), 0.3, EASE)
|
||||||
|
),
|
||||||
|
new KeyFrame(duration.divide(2),
|
||||||
|
new KeyValue(node.opacityProperty(), 0, EASE),
|
||||||
|
new KeyValue(node.scaleXProperty(), 0.3, EASE),
|
||||||
|
new KeyValue(node.scaleYProperty(), 0.3, EASE),
|
||||||
|
new KeyValue(node.scaleZProperty(), 0.3, EASE)
|
||||||
|
),
|
||||||
|
new KeyFrame(duration, new KeyValue(node.opacityProperty(), 0, EASE))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ import javafx.scene.control.ScrollPane;
|
|||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.ColumnConstraints;
|
import javafx.scene.layout.ColumnConstraints;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
|
||||||
import static javafx.scene.layout.Region.USE_COMPUTED_SIZE;
|
import static javafx.scene.layout.Region.USE_COMPUTED_SIZE;
|
||||||
import static javafx.scene.layout.Region.USE_PREF_SIZE;
|
import static javafx.scene.layout.Region.USE_PREF_SIZE;
|
||||||
@ -41,4 +42,14 @@ public final class Containers {
|
|||||||
constraints.setHgrow(hgrow);
|
constraints.setHgrow(hgrow);
|
||||||
return constraints;
|
return constraints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void usePrefWidth(Region region) {
|
||||||
|
region.setMinWidth(USE_PREF_SIZE);
|
||||||
|
region.setMaxWidth(USE_PREF_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void usePrefHeight(Region region) {
|
||||||
|
region.setMinHeight(USE_PREF_SIZE);
|
||||||
|
region.setMaxHeight(USE_PREF_SIZE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,6 @@ public class JColorUtils {
|
|||||||
* @param red red hex color in format RR or R
|
* @param red red hex color in format RR or R
|
||||||
* @param green green hex color in format GG or G
|
* @param green green hex color in format GG or G
|
||||||
* @param blue blue hex color in format BB or B
|
* @param blue blue hex color in format BB or B
|
||||||
*
|
|
||||||
* @return hex color in format #RRGGBB
|
* @return hex color in format #RRGGBB
|
||||||
*/
|
*/
|
||||||
public static String toColor(String red, String green, String blue) {
|
public static String toColor(String red, String green, String blue) {
|
||||||
@ -67,7 +66,6 @@ public class JColorUtils {
|
|||||||
* @param red red hex color in format RR or R
|
* @param red red hex color in format RR or R
|
||||||
* @param green green hex color in format GG or G
|
* @param green green hex color in format GG or G
|
||||||
* @param blue blue hex color in format BB or B
|
* @param blue blue hex color in format BB or B
|
||||||
*
|
|
||||||
* @return hex color in format #RGB or #RRGGBB
|
* @return hex color in format #RGB or #RRGGBB
|
||||||
*/
|
*/
|
||||||
public static String toColorShorthand(String red, String green,
|
public static String toColorShorthand(String red, String green,
|
||||||
@ -82,7 +80,6 @@ public class JColorUtils {
|
|||||||
* @param red red hex color in format RR or R
|
* @param red red hex color in format RR or R
|
||||||
* @param green green hex color in format GG or G
|
* @param green green hex color in format GG or G
|
||||||
* @param blue blue hex color in format BB or B
|
* @param blue blue hex color in format BB or B
|
||||||
*
|
|
||||||
* @return hex color in format #AARRGGBB
|
* @return hex color in format #AARRGGBB
|
||||||
*/
|
*/
|
||||||
public static String toColorWithAlpha(String red, String green,
|
public static String toColorWithAlpha(String red, String green,
|
||||||
@ -102,7 +99,6 @@ public class JColorUtils {
|
|||||||
* @param red red hex color in format RR or R
|
* @param red red hex color in format RR or R
|
||||||
* @param green green hex color in format GG or G
|
* @param green green hex color in format GG or G
|
||||||
* @param blue blue hex color in format BB or B
|
* @param blue blue hex color in format BB or B
|
||||||
*
|
|
||||||
* @return hex color in format #ARGB or #AARRGGBB
|
* @return hex color in format #ARGB or #AARRGGBB
|
||||||
*/
|
*/
|
||||||
public static String toColorShorthandWithAlpha(String red, String green,
|
public static String toColorShorthandWithAlpha(String red, String green,
|
||||||
@ -117,7 +113,6 @@ public class JColorUtils {
|
|||||||
* @param green green hex color in format GG or G
|
* @param green green hex color in format GG or G
|
||||||
* @param blue blue hex color in format BB or B
|
* @param blue blue hex color in format BB or B
|
||||||
* @param alpha alpha hex color in format AA or A, null to not include alpha
|
* @param alpha alpha hex color in format AA or A, null to not include alpha
|
||||||
*
|
|
||||||
* @return hex color in format #AARRGGBB or #RRGGBB
|
* @return hex color in format #AARRGGBB or #RRGGBB
|
||||||
*/
|
*/
|
||||||
public static String toColorWithAlpha(String red, String green, String blue,
|
public static String toColorWithAlpha(String red, String green, String blue,
|
||||||
@ -144,7 +139,6 @@ public class JColorUtils {
|
|||||||
* @param green green hex color in format GG or G
|
* @param green green hex color in format GG or G
|
||||||
* @param blue blue hex color in format BB or B
|
* @param blue blue hex color in format BB or B
|
||||||
* @param alpha alpha hex color in format AA or A, null to not include alpha
|
* @param alpha alpha hex color in format AA or A, null to not include alpha
|
||||||
*
|
|
||||||
* @return hex color in format #ARGB, #RGB, #AARRGGBB, or #RRGGBB
|
* @return hex color in format #ARGB, #RGB, #AARRGGBB, or #RRGGBB
|
||||||
*/
|
*/
|
||||||
public static String toColorShorthandWithAlpha(String red, String green,
|
public static String toColorShorthandWithAlpha(String red, String green,
|
||||||
@ -158,7 +152,6 @@ public class JColorUtils {
|
|||||||
* @param red red integer color inclusively between 0 and 255
|
* @param red red integer color inclusively between 0 and 255
|
||||||
* @param green green integer color inclusively between 0 and 255
|
* @param green green integer color inclusively between 0 and 255
|
||||||
* @param blue blue integer color inclusively between 0 and 255
|
* @param blue blue integer color inclusively between 0 and 255
|
||||||
*
|
|
||||||
* @return integer color
|
* @return integer color
|
||||||
*/
|
*/
|
||||||
public static int toColor(int red, int green, int blue) {
|
public static int toColor(int red, int green, int blue) {
|
||||||
@ -172,7 +165,6 @@ public class JColorUtils {
|
|||||||
* @param red red integer color inclusively between 0 and 255
|
* @param red red integer color inclusively between 0 and 255
|
||||||
* @param green green integer color inclusively between 0 and 255
|
* @param green green integer color inclusively between 0 and 255
|
||||||
* @param blue blue integer color inclusively between 0 and 255
|
* @param blue blue integer color inclusively between 0 and 255
|
||||||
*
|
|
||||||
* @return integer color
|
* @return integer color
|
||||||
*/
|
*/
|
||||||
public static int toColorWithAlpha(int red, int green, int blue) {
|
public static int toColorWithAlpha(int red, int green, int blue) {
|
||||||
@ -187,7 +179,6 @@ public class JColorUtils {
|
|||||||
* @param blue blue integer color inclusively between 0 and 255
|
* @param blue blue integer color inclusively between 0 and 255
|
||||||
* @param alpha alpha integer color inclusively between 0 and 255, -1 to not
|
* @param alpha alpha integer color inclusively between 0 and 255, -1 to not
|
||||||
* include alpha
|
* include alpha
|
||||||
*
|
|
||||||
* @return integer color
|
* @return integer color
|
||||||
*/
|
*/
|
||||||
public static int toColorWithAlpha(int red, int green, int blue,
|
public static int toColorWithAlpha(int red, int green, int blue,
|
||||||
@ -207,7 +198,6 @@ public class JColorUtils {
|
|||||||
* Convert the RGB integer to a hex single color
|
* Convert the RGB integer to a hex single color
|
||||||
*
|
*
|
||||||
* @param color integer color inclusively between 0 and 255
|
* @param color integer color inclusively between 0 and 255
|
||||||
*
|
|
||||||
* @return hex single color in format FF
|
* @return hex single color in format FF
|
||||||
*/
|
*/
|
||||||
public static String toHex(int color) {
|
public static String toHex(int color) {
|
||||||
@ -223,7 +213,6 @@ public class JColorUtils {
|
|||||||
* Convert the arithmetic RGB float to a hex single color
|
* Convert the arithmetic RGB float to a hex single color
|
||||||
*
|
*
|
||||||
* @param color float color inclusively between 0.0 and 1.0
|
* @param color float color inclusively between 0.0 and 1.0
|
||||||
*
|
|
||||||
* @return hex single color in format FF
|
* @return hex single color in format FF
|
||||||
*/
|
*/
|
||||||
public static String toHex(float color) {
|
public static String toHex(float color) {
|
||||||
@ -234,7 +223,6 @@ public class JColorUtils {
|
|||||||
* Convert the hex single color to a RGB integer
|
* Convert the hex single color to a RGB integer
|
||||||
*
|
*
|
||||||
* @param color hex single color in format FF or F
|
* @param color hex single color in format FF or F
|
||||||
*
|
|
||||||
* @return integer color inclusively between 0 and 255
|
* @return integer color inclusively between 0 and 255
|
||||||
*/
|
*/
|
||||||
public static int toRGB(String color) {
|
public static int toRGB(String color) {
|
||||||
@ -249,7 +237,6 @@ public class JColorUtils {
|
|||||||
* Convert the arithmetic RGB float to a RGB integer
|
* Convert the arithmetic RGB float to a RGB integer
|
||||||
*
|
*
|
||||||
* @param color float color inclusively between 0.0 and 1.0
|
* @param color float color inclusively between 0.0 and 1.0
|
||||||
*
|
|
||||||
* @return integer color inclusively between 0 and 255
|
* @return integer color inclusively between 0 and 255
|
||||||
*/
|
*/
|
||||||
public static int toRGB(float color) {
|
public static int toRGB(float color) {
|
||||||
@ -261,7 +248,6 @@ public class JColorUtils {
|
|||||||
* Convert the hex single color to an arithmetic RGB float
|
* Convert the hex single color to an arithmetic RGB float
|
||||||
*
|
*
|
||||||
* @param color hex single color in format FF or F
|
* @param color hex single color in format FF or F
|
||||||
*
|
|
||||||
* @return float color inclusively between 0.0 and 1.0
|
* @return float color inclusively between 0.0 and 1.0
|
||||||
*/
|
*/
|
||||||
public static float toArithmeticRGB(String color) {
|
public static float toArithmeticRGB(String color) {
|
||||||
@ -272,7 +258,6 @@ public class JColorUtils {
|
|||||||
* Convert the RGB integer to an arithmetic RGB float
|
* Convert the RGB integer to an arithmetic RGB float
|
||||||
*
|
*
|
||||||
* @param color integer color inclusively between 0 and 255
|
* @param color integer color inclusively between 0 and 255
|
||||||
*
|
|
||||||
* @return float color inclusively between 0.0 and 1.0
|
* @return float color inclusively between 0.0 and 1.0
|
||||||
*/
|
*/
|
||||||
public static float toArithmeticRGB(int color) {
|
public static float toArithmeticRGB(int color) {
|
||||||
@ -287,7 +272,6 @@ public class JColorUtils {
|
|||||||
* @param red red color inclusively between 0.0 and 1.0
|
* @param red red color inclusively between 0.0 and 1.0
|
||||||
* @param green green color inclusively between 0.0 and 1.0
|
* @param green green color inclusively between 0.0 and 1.0
|
||||||
* @param blue blue color inclusively between 0.0 and 1.0
|
* @param blue blue color inclusively between 0.0 and 1.0
|
||||||
*
|
|
||||||
* @return HSL array where: 0 = hue, 1 = saturation, 2 = lightness
|
* @return HSL array where: 0 = hue, 1 = saturation, 2 = lightness
|
||||||
*/
|
*/
|
||||||
public static float[] toHSL(float red, float green, float blue) {
|
public static float[] toHSL(float red, float green, float blue) {
|
||||||
@ -342,7 +326,6 @@ public class JColorUtils {
|
|||||||
* @param red red color inclusively between 0 and 255
|
* @param red red color inclusively between 0 and 255
|
||||||
* @param green green color inclusively between 0 and 255
|
* @param green green color inclusively between 0 and 255
|
||||||
* @param blue blue color inclusively between 0 and 255
|
* @param blue blue color inclusively between 0 and 255
|
||||||
*
|
|
||||||
* @return HSL array where: 0 = hue, 1 = saturation, 2 = lightness
|
* @return HSL array where: 0 = hue, 1 = saturation, 2 = lightness
|
||||||
*/
|
*/
|
||||||
public static float[] toHSL(int red, int green, int blue) {
|
public static float[] toHSL(int red, int green, int blue) {
|
||||||
@ -357,7 +340,6 @@ public class JColorUtils {
|
|||||||
* @param hue hue value inclusively between 0.0 and 360.0
|
* @param hue hue value inclusively between 0.0 and 360.0
|
||||||
* @param saturation saturation inclusively between 0.0 and 1.0
|
* @param saturation saturation inclusively between 0.0 and 1.0
|
||||||
* @param lightness lightness inclusively between 0.0 and 1.0
|
* @param lightness lightness inclusively between 0.0 and 1.0
|
||||||
*
|
|
||||||
* @return arithmetic RGB array where: 0 = red, 1 = green, 2 = blue
|
* @return arithmetic RGB array where: 0 = red, 1 = green, 2 = blue
|
||||||
*/
|
*/
|
||||||
public static float[] toArithmeticRGB(float hue, float saturation,
|
public static float[] toArithmeticRGB(float hue, float saturation,
|
||||||
@ -389,7 +371,6 @@ public class JColorUtils {
|
|||||||
* @param hue hue value inclusively between 0.0 and 360.0
|
* @param hue hue value inclusively between 0.0 and 360.0
|
||||||
* @param saturation saturation inclusively between 0.0 and 1.0
|
* @param saturation saturation inclusively between 0.0 and 1.0
|
||||||
* @param lightness lightness inclusively between 0.0 and 1.0
|
* @param lightness lightness inclusively between 0.0 and 1.0
|
||||||
*
|
|
||||||
* @return RGB integer array where: 0 = red, 1 = green, 2 = blue
|
* @return RGB integer array where: 0 = red, 1 = green, 2 = blue
|
||||||
*/
|
*/
|
||||||
public static int[] toRGB(float hue, float saturation, float lightness) {
|
public static int[] toRGB(float hue, float saturation, float lightness) {
|
||||||
@ -403,7 +384,6 @@ public class JColorUtils {
|
|||||||
* @param t1 t1
|
* @param t1 t1
|
||||||
* @param t2 t2
|
* @param t2 t2
|
||||||
* @param hue hue
|
* @param hue hue
|
||||||
*
|
|
||||||
* @return arithmetic RGB value
|
* @return arithmetic RGB value
|
||||||
*/
|
*/
|
||||||
private static float hslConvert(float t1, float t2, float hue) {
|
private static float hslConvert(float t1, float t2, float hue) {
|
||||||
@ -430,7 +410,6 @@ public class JColorUtils {
|
|||||||
* Get the hex red color from the hex string
|
* Get the hex red color from the hex string
|
||||||
*
|
*
|
||||||
* @param hex hex color
|
* @param hex hex color
|
||||||
*
|
|
||||||
* @return hex red color in format RR
|
* @return hex red color in format RR
|
||||||
*/
|
*/
|
||||||
public static String getRed(String hex) {
|
public static String getRed(String hex) {
|
||||||
@ -441,7 +420,6 @@ public class JColorUtils {
|
|||||||
* Get the hex green color from the hex string
|
* Get the hex green color from the hex string
|
||||||
*
|
*
|
||||||
* @param hex hex color
|
* @param hex hex color
|
||||||
*
|
|
||||||
* @return hex green color in format GG
|
* @return hex green color in format GG
|
||||||
*/
|
*/
|
||||||
public static String getGreen(String hex) {
|
public static String getGreen(String hex) {
|
||||||
@ -452,7 +430,6 @@ public class JColorUtils {
|
|||||||
* Get the hex blue color from the hex string
|
* Get the hex blue color from the hex string
|
||||||
*
|
*
|
||||||
* @param hex hex color
|
* @param hex hex color
|
||||||
*
|
|
||||||
* @return hex blue color in format BB
|
* @return hex blue color in format BB
|
||||||
*/
|
*/
|
||||||
public static String getBlue(String hex) {
|
public static String getBlue(String hex) {
|
||||||
@ -463,7 +440,6 @@ public class JColorUtils {
|
|||||||
* Get the hex alpha color from the hex string if it exists
|
* Get the hex alpha color from the hex string if it exists
|
||||||
*
|
*
|
||||||
* @param hex hex color
|
* @param hex hex color
|
||||||
*
|
|
||||||
* @return hex alpha color in format AA or null
|
* @return hex alpha color in format AA or null
|
||||||
*/
|
*/
|
||||||
public static String getAlpha(String hex) {
|
public static String getAlpha(String hex) {
|
||||||
@ -475,7 +451,6 @@ public class JColorUtils {
|
|||||||
*
|
*
|
||||||
* @param hex hex color
|
* @param hex hex color
|
||||||
* @param colorIndex red=0, green=1, blue=2, alpha=-1
|
* @param colorIndex red=0, green=1, blue=2, alpha=-1
|
||||||
*
|
|
||||||
* @return hex single color in format FF or null
|
* @return hex single color in format FF or null
|
||||||
*/
|
*/
|
||||||
private static String getHexSingle(String hex, int colorIndex) {
|
private static String getHexSingle(String hex, int colorIndex) {
|
||||||
@ -512,7 +487,6 @@ public class JColorUtils {
|
|||||||
* Get the red color from color integer
|
* Get the red color from color integer
|
||||||
*
|
*
|
||||||
* @param color color integer
|
* @param color color integer
|
||||||
*
|
|
||||||
* @return red color
|
* @return red color
|
||||||
*/
|
*/
|
||||||
public static int getRed(int color) {
|
public static int getRed(int color) {
|
||||||
@ -523,7 +497,6 @@ public class JColorUtils {
|
|||||||
* Get the green color from color integer
|
* Get the green color from color integer
|
||||||
*
|
*
|
||||||
* @param color color integer
|
* @param color color integer
|
||||||
*
|
|
||||||
* @return green color
|
* @return green color
|
||||||
*/
|
*/
|
||||||
public static int getGreen(int color) {
|
public static int getGreen(int color) {
|
||||||
@ -534,7 +507,6 @@ public class JColorUtils {
|
|||||||
* Get the blue color from color integer
|
* Get the blue color from color integer
|
||||||
*
|
*
|
||||||
* @param color color integer
|
* @param color color integer
|
||||||
*
|
|
||||||
* @return blue color
|
* @return blue color
|
||||||
*/
|
*/
|
||||||
public static int getBlue(int color) {
|
public static int getBlue(int color) {
|
||||||
@ -545,7 +517,6 @@ public class JColorUtils {
|
|||||||
* Get the alpha color from color integer
|
* Get the alpha color from color integer
|
||||||
*
|
*
|
||||||
* @param color color integer
|
* @param color color integer
|
||||||
*
|
|
||||||
* @return alpha color
|
* @return alpha color
|
||||||
*/
|
*/
|
||||||
public static int getAlpha(int color) {
|
public static int getAlpha(int color) {
|
||||||
@ -556,7 +527,6 @@ public class JColorUtils {
|
|||||||
* Shorthand the hex color if possible
|
* Shorthand the hex color if possible
|
||||||
*
|
*
|
||||||
* @param color hex color
|
* @param color hex color
|
||||||
*
|
|
||||||
* @return shorthand hex color or original value
|
* @return shorthand hex color or original value
|
||||||
*/
|
*/
|
||||||
public static String shorthandHex(String color) {
|
public static String shorthandHex(String color) {
|
||||||
@ -589,7 +559,6 @@ public class JColorUtils {
|
|||||||
* Expand the hex if it is in shorthand
|
* Expand the hex if it is in shorthand
|
||||||
*
|
*
|
||||||
* @param color hex color
|
* @param color hex color
|
||||||
*
|
|
||||||
* @return expanded hex color or original value
|
* @return expanded hex color or original value
|
||||||
*/
|
*/
|
||||||
public static String expandShorthandHex(String color) {
|
public static String expandShorthandHex(String color) {
|
||||||
@ -615,7 +584,6 @@ public class JColorUtils {
|
|||||||
* Shorthand the hex single color if possible
|
* Shorthand the hex single color if possible
|
||||||
*
|
*
|
||||||
* @param color hex single color
|
* @param color hex single color
|
||||||
*
|
|
||||||
* @return shorthand hex color or original value
|
* @return shorthand hex color or original value
|
||||||
*/
|
*/
|
||||||
public static String shorthandHexSingle(String color) {
|
public static String shorthandHexSingle(String color) {
|
||||||
@ -632,7 +600,6 @@ public class JColorUtils {
|
|||||||
* Expand the hex single if it is in shorthand
|
* Expand the hex single if it is in shorthand
|
||||||
*
|
*
|
||||||
* @param color hex single color
|
* @param color hex single color
|
||||||
*
|
|
||||||
* @return expanded hex color or original value
|
* @return expanded hex color or original value
|
||||||
*/
|
*/
|
||||||
public static String expandShorthandHexSingle(String color) {
|
public static String expandShorthandHexSingle(String color) {
|
||||||
@ -647,7 +614,6 @@ public class JColorUtils {
|
|||||||
* Check if the hex color value is valid
|
* Check if the hex color value is valid
|
||||||
*
|
*
|
||||||
* @param color hex color
|
* @param color hex color
|
||||||
*
|
|
||||||
* @return true if valid
|
* @return true if valid
|
||||||
*/
|
*/
|
||||||
public static boolean isValidHex(String color) {
|
public static boolean isValidHex(String color) {
|
||||||
@ -671,7 +637,6 @@ public class JColorUtils {
|
|||||||
* Check if the hex single color value is valid
|
* Check if the hex single color value is valid
|
||||||
*
|
*
|
||||||
* @param color hex single color
|
* @param color hex single color
|
||||||
*
|
|
||||||
* @return true if valid
|
* @return true if valid
|
||||||
*/
|
*/
|
||||||
public static boolean isValidHexSingle(String color) {
|
public static boolean isValidHexSingle(String color) {
|
||||||
@ -694,7 +659,6 @@ public class JColorUtils {
|
|||||||
* Check if the RGB integer color is valid, inclusively between 0 and 255
|
* Check if the RGB integer color is valid, inclusively between 0 and 255
|
||||||
*
|
*
|
||||||
* @param color decimal color
|
* @param color decimal color
|
||||||
*
|
|
||||||
* @return true if valid
|
* @return true if valid
|
||||||
*/
|
*/
|
||||||
public static boolean isValidRGB(int color) {
|
public static boolean isValidRGB(int color) {
|
||||||
@ -719,7 +683,6 @@ public class JColorUtils {
|
|||||||
* and 1.0
|
* and 1.0
|
||||||
*
|
*
|
||||||
* @param color decimal color
|
* @param color decimal color
|
||||||
*
|
|
||||||
* @return true if valid
|
* @return true if valid
|
||||||
*/
|
*/
|
||||||
public static boolean isValidArithmeticRGB(float color) {
|
public static boolean isValidArithmeticRGB(float color) {
|
||||||
@ -745,7 +708,6 @@ public class JColorUtils {
|
|||||||
* 360.0
|
* 360.0
|
||||||
*
|
*
|
||||||
* @param hue hue value
|
* @param hue hue value
|
||||||
*
|
|
||||||
* @return true if valid
|
* @return true if valid
|
||||||
*/
|
*/
|
||||||
public static boolean isValidHue(float hue) {
|
public static boolean isValidHue(float hue) {
|
||||||
@ -770,7 +732,6 @@ public class JColorUtils {
|
|||||||
* and 1.0
|
* and 1.0
|
||||||
*
|
*
|
||||||
* @param saturation saturation value
|
* @param saturation saturation value
|
||||||
*
|
|
||||||
* @return true if valid
|
* @return true if valid
|
||||||
*/
|
*/
|
||||||
public static boolean isValidSaturation(float saturation) {
|
public static boolean isValidSaturation(float saturation) {
|
||||||
@ -796,7 +757,6 @@ public class JColorUtils {
|
|||||||
* and 1.0
|
* and 1.0
|
||||||
*
|
*
|
||||||
* @param lightness lightness value
|
* @param lightness lightness value
|
||||||
*
|
|
||||||
* @return true if valid
|
* @return true if valid
|
||||||
*/
|
*/
|
||||||
public static boolean isValidLightness(float lightness) {
|
public static boolean isValidLightness(float lightness) {
|
||||||
@ -899,4 +859,21 @@ public class JColorUtils {
|
|||||||
fgColor.getBlue(),
|
fgColor.getBlue(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static float[] toHSL(Color color) {
|
||||||
|
return JColorUtils.toHSL(
|
||||||
|
(float) color.getRed(),
|
||||||
|
(float) color.getGreen(),
|
||||||
|
(float) color.getBlue()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toHexWithAlpha(Color color) {
|
||||||
|
return JColor.color(
|
||||||
|
(float) color.getRed(),
|
||||||
|
(float) color.getGreen(),
|
||||||
|
(float) color.getBlue(),
|
||||||
|
(float) color.getOpacity()
|
||||||
|
).getColorHexShorthandWithAlpha();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
38
sampler/src/main/java/atlantafx/sampler/util/NodeUtils.java
Normal file
38
sampler/src/main/java/atlantafx/sampler/util/NodeUtils.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
package atlantafx.sampler.util;
|
||||||
|
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.input.MouseButton;
|
||||||
|
import javafx.scene.input.MouseEvent;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class NodeUtils {
|
||||||
|
|
||||||
|
public static void toggleVisibility(Node node, boolean on) {
|
||||||
|
node.setVisible(on);
|
||||||
|
node.setManaged(on);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDoubleClick(MouseEvent e) {
|
||||||
|
return e.getButton().equals(MouseButton.PRIMARY) && e.getClickCount() == 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getChildByIndex(Parent parent, int index, Class<T> contentType) {
|
||||||
|
List<Node> children = parent.getChildrenUnmodifiable();
|
||||||
|
if (index < 0 || index >= children.size()) { return null; }
|
||||||
|
Node node = children.get(index);
|
||||||
|
return contentType.isInstance(node) ? contentType.cast(node) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDescendant(Node ancestor, Node descendant) {
|
||||||
|
if (ancestor == null) { return true; }
|
||||||
|
|
||||||
|
while (descendant != null) {
|
||||||
|
if (descendant == ancestor) { return true; }
|
||||||
|
descendant = descendant.getParent();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -3,3 +3,4 @@
|
|||||||
@use "sidebar";
|
@use "sidebar";
|
||||||
@use "page";
|
@use "page";
|
||||||
@use "components";
|
@use "components";
|
||||||
|
@use "overlay";
|
@ -0,0 +1,31 @@
|
|||||||
|
.overlay {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
|
||||||
|
>.scroll-pane {
|
||||||
|
>.viewport {
|
||||||
|
>* {
|
||||||
|
>.scrollable-content {
|
||||||
|
-fx-background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-dialog {
|
||||||
|
-fx-background-color: -color-bg-default;
|
||||||
|
-fx-background-radius: 5px;
|
||||||
|
-fx-border-radius: 5px;
|
||||||
|
-fx-border-width: 1px;
|
||||||
|
-fx-border-color: -color-border-default;
|
||||||
|
|
||||||
|
>.header {
|
||||||
|
-fx-padding: 10px 5px 5px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.footer {
|
||||||
|
-fx-border-width: 1 0 0 0;
|
||||||
|
-fx-border-color: -color-border-default;
|
||||||
|
-fx-padding: 10;
|
||||||
|
}
|
||||||
|
}
|
@ -45,88 +45,4 @@ $color-wsag-fg: white;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>.contrast-checker-area {
|
|
||||||
-fx-padding: 0 0 0 -20px;
|
|
||||||
|
|
||||||
>.contrast-checker {
|
|
||||||
-fx-background-color: -color-contrast-checker-bg;
|
|
||||||
-fx-hgap: 40px;
|
|
||||||
-fx-vgap: 20px;
|
|
||||||
-fx-padding: 20px 20px 40px 20px;
|
|
||||||
|
|
||||||
.label {
|
|
||||||
-fx-text-fill: -color-contrast-checker-fg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-field {
|
|
||||||
-fx-background-insets: 0;
|
|
||||||
-fx-background-color: transparent;
|
|
||||||
-fx-background-radius: 0;
|
|
||||||
-fx-text-fill: -color-contrast-checker-fg;
|
|
||||||
-fx-border-color: -color-contrast-checker-fg;
|
|
||||||
-fx-border-width: 0 0 1 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ikonli-font-icon {
|
|
||||||
-fx-icon-color: -color-contrast-checker-fg;
|
|
||||||
-fx-fill: -color-contrast-checker-fg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider {
|
|
||||||
>.thumb {
|
|
||||||
-fx-background-color: -color-contrast-checker-fg;
|
|
||||||
}
|
|
||||||
|
|
||||||
>.track {
|
|
||||||
-fx-background-color: transparent, -color-contrast-checker-fg;
|
|
||||||
-fx-opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-box {
|
|
||||||
>.text {
|
|
||||||
-fx-font-size: 4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
>.ratio {
|
|
||||||
-fx-font-size: 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
>.wsag-box>*>.wsag-label {
|
|
||||||
-fx-padding: 0.5em 1em 0.5em 1em;
|
|
||||||
-fx-background-color: $color-wsag-bg-failed;
|
|
||||||
-fx-background-radius: 6px;
|
|
||||||
-fx-text-fill: $color-wsag-fg;
|
|
||||||
|
|
||||||
&:passed {
|
|
||||||
-fx-background-color: $color-wsag-bg-passed;
|
|
||||||
}
|
|
||||||
|
|
||||||
>.ikonli-font-icon {
|
|
||||||
-fx-fill: $color-wsag-fg;
|
|
||||||
-fx-icon-color: $color-wsag-fg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
@use "color-palette" as palette;
|
||||||
|
|
||||||
|
.contrast-checker {
|
||||||
|
-fx-background-color: -color-contrast-checker-bg;
|
||||||
|
-fx-hgap: 40px;
|
||||||
|
-fx-vgap: 20px;
|
||||||
|
-fx-padding: 20px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
-fx-text-fill: -color-contrast-checker-fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-field {
|
||||||
|
-fx-background-insets: 0;
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-background-radius: 0;
|
||||||
|
-fx-text-fill: -color-contrast-checker-fg;
|
||||||
|
-fx-border-color: -color-contrast-checker-fg;
|
||||||
|
-fx-border-width: 0 0 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
-color-button-bg: transparent;
|
||||||
|
-color-button-fg: -color-contrast-checker-fg;
|
||||||
|
-color-button-border: transparent;
|
||||||
|
|
||||||
|
-color-button-bg-hover: transparent;
|
||||||
|
-color-button-fg-hover: -color-contrast-checker-fg;
|
||||||
|
-color-button-border-hover: transparent;
|
||||||
|
|
||||||
|
-color-button-bg-focused: transparent;
|
||||||
|
-color-button-fg-focused: -color-contrast-checker-fg;
|
||||||
|
-color-button-border-focused: transparent;
|
||||||
|
|
||||||
|
-color-button-bg-pressed: transparent;
|
||||||
|
-color-button-fg-pressed: -color-contrast-checker-fg;
|
||||||
|
-color-button-border-pressed: transparent;
|
||||||
|
|
||||||
|
-fx-border-width: 1px;
|
||||||
|
-fx-border-color: -color-contrast-checker-fg;
|
||||||
|
-fx-border-radius: 4px;
|
||||||
|
|
||||||
|
&:armed,
|
||||||
|
&:focused:armed {
|
||||||
|
-fx-border-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ikonli-font-icon {
|
||||||
|
-fx-icon-color: -color-contrast-checker-fg;
|
||||||
|
-fx-fill: -color-contrast-checker-fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
>.thumb {
|
||||||
|
-fx-background-color: -color-contrast-checker-fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.track {
|
||||||
|
-fx-background-color: transparent, -color-contrast-checker-fg;
|
||||||
|
-fx-opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-box {
|
||||||
|
-fx-padding: 0 40px 0 0;
|
||||||
|
|
||||||
|
>.text {
|
||||||
|
-fx-font-size: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ratio {
|
||||||
|
-fx-font-size: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsag-box>*>.wsag-label {
|
||||||
|
-fx-padding: 0.5em 1em 0.5em 1em;
|
||||||
|
-fx-background-color: palette.$color-wsag-bg-failed;
|
||||||
|
-fx-background-radius: 4px;
|
||||||
|
-fx-text-fill: palette.$color-wsag-fg;
|
||||||
|
|
||||||
|
&:passed {
|
||||||
|
-fx-background-color: palette.$color-wsag-bg-passed;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ikonli-font-icon {
|
||||||
|
-fx-fill: palette.$color-wsag-fg;
|
||||||
|
-fx-icon-color: palette.$color-wsag-fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.contrast-checker-dialog {
|
||||||
|
-color-dialog-bg: white;
|
||||||
|
-color-dialog-fg: black;
|
||||||
|
|
||||||
|
>.header {
|
||||||
|
-fx-background-color: -color-dialog-bg;
|
||||||
|
|
||||||
|
>.title {
|
||||||
|
-fx-text-fill: -color-dialog-fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.close-button {
|
||||||
|
-color-button-fg: -color-dialog-fg;
|
||||||
|
-color-button-bg-hover: derive(-color-dialog-bg, 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-fx-border-color: -color-dialog-bg;
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
@use "color-palette";
|
@use "color-palette";
|
||||||
|
@use "contrast-checker";
|
||||||
@use "quick-config-menu";
|
@use "quick-config-menu";
|
Loading…
Reference in New Issue
Block a user