Improve utility methods in Styles class
- Added more methods to manipulate style properties. - Added unit tests.
This commit is contained in:
parent
c97142e9bf
commit
561bd78d23
@ -2,6 +2,9 @@
|
||||
|
||||
package atlantafx.base.theme;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Objects;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.scene.Node;
|
||||
@ -14,6 +17,8 @@ import javafx.scene.control.TabPane;
|
||||
@SuppressWarnings("unused")
|
||||
public final class Styles {
|
||||
|
||||
public static final String DATA_URI_PREFIX = "data:base64,";
|
||||
|
||||
// Colors
|
||||
|
||||
public static final String ACCENT = "accent";
|
||||
@ -92,6 +97,10 @@ public final class Styles {
|
||||
/**
|
||||
* Adds given style class to the node if it's not present, otherwise
|
||||
* removes it.
|
||||
*
|
||||
* @param node the target node
|
||||
* @param styleClass the style class to be toggled
|
||||
* @throws NullPointerException if node or style class is null
|
||||
*/
|
||||
public static void toggleStyleClass(Node node, String styleClass) {
|
||||
if (node == null) {
|
||||
@ -102,7 +111,7 @@ public final class Styles {
|
||||
}
|
||||
|
||||
int idx = node.getStyleClass().indexOf(styleClass);
|
||||
if (idx > 0) {
|
||||
if (idx >= 0) {
|
||||
node.getStyleClass().remove(idx);
|
||||
} else {
|
||||
node.getStyleClass().add(styleClass);
|
||||
@ -113,6 +122,11 @@ public final class Styles {
|
||||
* Adds given style class to the node and removes the excluded classes.
|
||||
* This method is supposed to be used when only one from a set of classes
|
||||
* have to be present at once.
|
||||
*
|
||||
* @param node the target node
|
||||
* @param styleClass the style class to be toggled
|
||||
* @param excludes the style classes to be excluded
|
||||
* @throws NullPointerException if node or styleClass is null
|
||||
*/
|
||||
public static void addStyleClass(Node node, String styleClass, String... excludes) {
|
||||
if (node == null) {
|
||||
@ -125,13 +139,21 @@ public final class Styles {
|
||||
if (excludes != null && excludes.length > 0) {
|
||||
node.getStyleClass().removeAll(excludes);
|
||||
}
|
||||
|
||||
if (!node.getStyleClass().contains(styleClass)) {
|
||||
node.getStyleClass().add(styleClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates given pseudo-class to the node and deactivates the excluded pseudo-classes.
|
||||
* This method is supposed to be used when only one from a set of pseudo-classes
|
||||
* have to be present at once.
|
||||
*
|
||||
* @param node the node to activate the pseudo-class on
|
||||
* @param pseudoClass the pseudo-class to be activated
|
||||
* @param excludes the pseudo-classes to be deactivated
|
||||
* @throws NullPointerException if node or pseudo-class is null
|
||||
*/
|
||||
public static void activatePseudoClass(Node node, PseudoClass pseudoClass, PseudoClass... excludes) {
|
||||
if (node == null) {
|
||||
@ -149,17 +171,88 @@ public final class Styles {
|
||||
node.pseudoClassStateChanged(pseudoClass, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends CSS style declaration to the specified node.
|
||||
* There's no check for duplicates, so the CSS declarations with the same property
|
||||
* name can be appended multiple times.
|
||||
*
|
||||
* @param node the node to append the new style declaration
|
||||
* @param prop CSS property name
|
||||
* @param value CSS property value
|
||||
* @throws NullPointerException if node is null
|
||||
*/
|
||||
public static void appendStyle(Node node, String prop, String value) {
|
||||
if (node == null) {
|
||||
throw new NullPointerException("Node cannot be null!");
|
||||
}
|
||||
|
||||
if (prop == null || prop.isBlank() || value == null || value.isBlank()) {
|
||||
System.err.printf("Ignoring invalid style: property = '%s', value = '%s'%n", prop, value);
|
||||
return;
|
||||
}
|
||||
|
||||
var style = Objects.requireNonNullElse(node.getStyle(), "");
|
||||
if (!style.endsWith(";")) {
|
||||
if (!style.isEmpty() && !style.endsWith(";")) {
|
||||
style += ";";
|
||||
}
|
||||
style = style + prop.trim() + ":" + value.trim() + ";";
|
||||
node.setStyle(style);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified CSS style declaration from the specified node.
|
||||
*
|
||||
* @param node the node to remove the style from
|
||||
* @param prop the name of the style property to remove
|
||||
* @throws NullPointerException if node is null
|
||||
*/
|
||||
public static void removeStyle(Node node, String prop) {
|
||||
if (node == null) {
|
||||
throw new NullPointerException("Node cannot be null!");
|
||||
}
|
||||
|
||||
var currentStyle = node.getStyle();
|
||||
if (currentStyle == null || currentStyle.isBlank()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prop == null || prop.isBlank()) {
|
||||
System.err.printf("Ignoring invalid property = '%s'%n", prop);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] stylePairs = currentStyle.split(";");
|
||||
var newStyle = new StringBuilder();
|
||||
|
||||
for (var s : stylePairs) {
|
||||
String[] styleParts = s.split(":");
|
||||
if (!styleParts[0].trim().equals(prop)) {
|
||||
newStyle.append(s);
|
||||
newStyle.append(";");
|
||||
}
|
||||
}
|
||||
|
||||
node.setStyle(newStyle.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a CSS string to a Base64-encoded data URI. The resulting string is
|
||||
* an inline data URI that can be applied to any node in the following manner:
|
||||
*
|
||||
* <pre>{@code}
|
||||
* var dataUri = Styles.toDataURI();
|
||||
* node.getStylesheets().add(dataUri);
|
||||
* node.getStylesheets().contains(dataUri);
|
||||
* node.getStylesheets().remove(dataUri);
|
||||
* </pre>
|
||||
*
|
||||
* @param css the CSS string to encode
|
||||
* @return the resulting data URI string
|
||||
*/
|
||||
public static String toDataURI(String css) {
|
||||
if (css == null) {
|
||||
throw new NullPointerException("CSS string cannot be null!");
|
||||
}
|
||||
return DATA_URI_PREFIX + new String(Base64.getEncoder().encode(css.getBytes(UTF_8)), UTF_8);
|
||||
}
|
||||
}
|
||||
|
332
base/src/test/java/atlantafx/base/theme/StylesTest.java
Normal file
332
base/src/test/java/atlantafx/base/theme/StylesTest.java
Normal file
@ -0,0 +1,332 @@
|
||||
package atlantafx.base.theme;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
|
||||
|
||||
import java.util.Base64;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.scene.layout.Region;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class StylesTest {
|
||||
|
||||
PseudoClass pcFirst = PseudoClass.getPseudoClass("first");
|
||||
PseudoClass pcSecond = PseudoClass.getPseudoClass("second");
|
||||
PseudoClass pcExcluded = PseudoClass.getPseudoClass("excluded");
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
public void testToggleStyleClassNPE() {
|
||||
assertThatNullPointerException().isThrownBy(
|
||||
() -> Styles.toggleStyleClass(new Region(), null)
|
||||
);
|
||||
assertThatNullPointerException().isThrownBy(
|
||||
() -> Styles.toggleStyleClass(null, "any")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToggleStyleClassOn() {
|
||||
var node = new Region();
|
||||
node.getStyleClass().add("first");
|
||||
assertThat(node.getStyleClass()).containsExactly("first");
|
||||
|
||||
Styles.toggleStyleClass(node, "second");
|
||||
assertThat(node.getStyleClass()).containsExactly("first", "second");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToggleStyleClassMultipleOn() {
|
||||
var node = new Region();
|
||||
node.getStyleClass().addAll("first", "second", "third");
|
||||
assertThat(node.getStyleClass()).containsExactly("first", "second", "third");
|
||||
|
||||
Styles.toggleStyleClass(node, "fourth");
|
||||
assertThat(node.getStyleClass()).containsExactly("first", "second", "third", "fourth");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToggleStyleClassOff() {
|
||||
var node = new Region();
|
||||
node.getStyleClass().add("sole");
|
||||
assertThat(node.getStyleClass()).containsExactly("sole");
|
||||
|
||||
Styles.toggleStyleClass(node, "sole");
|
||||
assertThat(node.getStyleClass()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToggleStyleClassMultipleOff() {
|
||||
var node = new Region();
|
||||
node.getStyleClass().addAll("first", "second", "third");
|
||||
assertThat(node.getStyleClass()).containsExactly("first", "second", "third");
|
||||
|
||||
Styles.toggleStyleClass(node, "second");
|
||||
assertThat(node.getStyleClass()).containsExactly("first", "third");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
public void testAddStyleClassClassNPE() {
|
||||
assertThatNullPointerException().isThrownBy(
|
||||
() -> Styles.addStyleClass(new Region(), null)
|
||||
);
|
||||
assertThatNullPointerException().isThrownBy(
|
||||
() -> Styles.addStyleClass(null, "any")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddStyleClassAdds() {
|
||||
var node = new Region();
|
||||
node.getStyleClass().addAll("first");
|
||||
assertThat(node.getStyleClass()).containsExactly("first");
|
||||
|
||||
Styles.addStyleClass(node, "second");
|
||||
assertThat(node.getStyleClass()).containsExactly("first", "second");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddStyleClassExcludes() {
|
||||
var node = new Region();
|
||||
node.getStyleClass().addAll("first", "excluded");
|
||||
assertThat(node.getStyleClass()).containsExactly("first", "excluded");
|
||||
|
||||
Styles.addStyleClass(node, "second", "excluded");
|
||||
assertThat(node.getStyleClass()).containsExactly("first", "second");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddStyleClassIgnoresDuplicate() {
|
||||
var node = new Region();
|
||||
node.getStyleClass().addAll("first", "second", "excluded");
|
||||
assertThat(node.getStyleClass()).containsExactly("first", "second", "excluded");
|
||||
|
||||
Styles.addStyleClass(node, "second", "excluded");
|
||||
assertThat(node.getStyleClass()).containsExactly("first", "second");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
public void testActivatePseudoClassNPE() {
|
||||
assertThatNullPointerException().isThrownBy(
|
||||
() -> Styles.activatePseudoClass(new Region(), null)
|
||||
);
|
||||
assertThatNullPointerException().isThrownBy(
|
||||
() -> Styles.activatePseudoClass(null, pcFirst)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivatePseudoClassActivates() {
|
||||
var node = new Region();
|
||||
node.pseudoClassStateChanged(pcFirst, true);
|
||||
assertThat(node.getPseudoClassStates()).containsExactly(pcFirst);
|
||||
|
||||
Styles.activatePseudoClass(node, pcSecond);
|
||||
assertThat(node.getPseudoClassStates()).containsExactly(pcFirst, pcSecond);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivatePseudoClassExcludes() {
|
||||
var node = new Region();
|
||||
node.pseudoClassStateChanged(pcFirst, true);
|
||||
node.pseudoClassStateChanged(pcExcluded, true);
|
||||
assertThat(node.getPseudoClassStates()).containsExactly(pcFirst, pcExcluded);
|
||||
|
||||
Styles.activatePseudoClass(node, pcSecond, pcExcluded);
|
||||
assertThat(node.getPseudoClassStates()).containsExactly(pcFirst, pcSecond);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivatePseudoClassIgnoresDuplicate() {
|
||||
var node = new Region();
|
||||
node.pseudoClassStateChanged(pcFirst, true);
|
||||
node.pseudoClassStateChanged(pcSecond, true);
|
||||
node.pseudoClassStateChanged(pcExcluded, true);
|
||||
assertThat(node.getPseudoClassStates()).containsExactly(pcFirst, pcSecond, pcExcluded);
|
||||
|
||||
Styles.activatePseudoClass(node, pcSecond, pcExcluded);
|
||||
assertThat(node.getPseudoClassStates()).containsExactly(pcFirst, pcSecond);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
void testAppendStyleNullNode() {
|
||||
assertThatNullPointerException().isThrownBy(
|
||||
() -> Styles.appendStyle(null, "-fx-background-color", "red")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAppendStyleValid() {
|
||||
var node = new Region();
|
||||
Styles.appendStyle(node, "-fx-background-color", "red");
|
||||
assertThat(node.getStyle()).isEqualTo("-fx-background-color:red;");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAppendStyleEmptyProperty() {
|
||||
var node = new Region();
|
||||
Styles.appendStyle(node, "", "red");
|
||||
assertThat(node.getStyle()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAppendStyleEmptyValue() {
|
||||
var node = new Region();
|
||||
Styles.appendStyle(node, "-fx-background-color", "");
|
||||
assertThat(node.getStyle()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAppendStyleNullProperty() {
|
||||
var node = new Region();
|
||||
Styles.appendStyle(node, null, "red");
|
||||
assertThat(node.getStyle()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAppendStyleNullValue() {
|
||||
var node = new Region();
|
||||
Styles.appendStyle(node, "-fx-background-color", null);
|
||||
assertThat(node.getStyle()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAppendStyleMultipleProperties() {
|
||||
var node = new Region();
|
||||
Styles.appendStyle(node, "-fx-background-color", "red");
|
||||
Styles.appendStyle(node, "-fx-text-fill", "white");
|
||||
assertThat(node.getStyle()).isEqualTo("-fx-background-color:red;-fx-text-fill:white;");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAppendStyleDuplicateProperty() {
|
||||
var node = new Region();
|
||||
Styles.appendStyle(node, "-fx-background-color", "red");
|
||||
Styles.appendStyle(node, "-fx-background-color", "blue");
|
||||
// that's it, "append" appends, no check for duplicates
|
||||
assertThat(node.getStyle()).isEqualTo("-fx-background-color:red;-fx-background-color:blue;");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Test
|
||||
void testRemoveStyleValidProperty() {
|
||||
var node = new Region();
|
||||
node.setStyle("-fx-background-color:red;-fx-text-fill:white;");
|
||||
Styles.removeStyle(node, "-fx-background-color");
|
||||
assertThat(node.getStyle())
|
||||
.contains("-fx-text-fill:white;")
|
||||
.doesNotContain("-fx-background-color:red;");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveStyleNonexistentProperty() {
|
||||
var node = new Region();
|
||||
node.setStyle("-fx-background-color:red;");
|
||||
Styles.removeStyle(node, "-fx-text-fill");
|
||||
assertThat(node.getStyle()).contains("-fx-background-color:red;");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveStyleNullProperty() {
|
||||
var node = new Region();
|
||||
node.setStyle("-fx-background-color:red;");
|
||||
Styles.removeStyle(node, null);
|
||||
assertThat(node.getStyle()).contains("-fx-background-color:red;");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveStyleEmptyProperty() {
|
||||
var node = new Region();
|
||||
node.setStyle("-fx-background-color:red;");
|
||||
Styles.removeStyle(node, "");
|
||||
assertThat(node.getStyle()).contains("-fx-background-color:red;");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
void testRemoveStyleNullNode() {
|
||||
assertThatNullPointerException().isThrownBy(
|
||||
() -> Styles.removeStyle(null, "-fx-background-color")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveStyleFromEmptyNode() {
|
||||
var node = new Region();
|
||||
Styles.removeStyle(node, "-fx-background-color");
|
||||
assertThat(node.getStyle()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveMultipleStyles() {
|
||||
var node = new Region();
|
||||
node.setStyle("-fx-background-color:red;-fx-text-fill:white;");
|
||||
Styles.removeStyle(node, "-fx-background-color");
|
||||
Styles.removeStyle(node, "-fx-text-fill");
|
||||
assertThat(node.getStyle()).isEmpty();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Test
|
||||
void testToDataURIWithValidCSS() {
|
||||
String css = "body { font-size: 16px; }";
|
||||
|
||||
String dataUri = Styles.toDataURI(css);
|
||||
byte[] decodedBytes = Base64.getDecoder().decode(dataUri.substring(dataUri.indexOf(",") + 1));
|
||||
|
||||
assertThat(dataUri).startsWith(Styles.DATA_URI_PREFIX);
|
||||
assertThat(new String(decodedBytes)).isEqualTo(css);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToDataURIWithEmptyCSS() {
|
||||
String css = "";
|
||||
|
||||
String dataUri = Styles.toDataURI(css);
|
||||
byte[] decodedBytes = Base64.getDecoder().decode(dataUri.substring(dataUri.indexOf(",") + 1));
|
||||
|
||||
assertThat(dataUri).startsWith(Styles.DATA_URI_PREFIX);
|
||||
assertThat(new String(decodedBytes)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
void testToDataURIWithNullCSS() {
|
||||
assertThatNullPointerException().isThrownBy(
|
||||
() -> Styles.toDataURI(null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToDataURIWithWhitespaceCSS() {
|
||||
String css = " ";
|
||||
|
||||
String dataUri = Styles.toDataURI(css);
|
||||
byte[] decodedBytes = Base64.getDecoder().decode(dataUri.substring(dataUri.indexOf(",") + 1));
|
||||
|
||||
assertThat(dataUri).startsWith(Styles.DATA_URI_PREFIX);
|
||||
assertThat(new String(decodedBytes)).isEqualTo(css);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToDataURIWithSpecialCharactersCSS() {
|
||||
String css = "#id { background-image: url('https://example.com/bg.png'); }";
|
||||
|
||||
String dataUri = Styles.toDataURI(css);
|
||||
byte[] decodedBytes = Base64.getDecoder().decode(dataUri.substring(dataUri.indexOf(",") + 1));
|
||||
|
||||
assertThat(dataUri).startsWith(Styles.DATA_URI_PREFIX);
|
||||
assertThat(new String(decodedBytes)).isEqualTo(css);
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ import atlantafx.base.util.BBCodeParser;
|
||||
import atlantafx.sampler.page.ExampleBox;
|
||||
import atlantafx.sampler.page.OutlinePage;
|
||||
import atlantafx.sampler.page.Snippet;
|
||||
import atlantafx.sampler.theme.CSSFragment;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
@ -395,7 +394,7 @@ public final class ButtonPage extends OutlinePage {
|
||||
// -fx-font-size: 32px;
|
||||
// -fx-icon-size: 32px;
|
||||
// }
|
||||
new CSSFragment(dataClass).addTo(iconBtn);
|
||||
iconBtn.getStylesheets().add(Styles.toDataURI(dataClass));
|
||||
//snippet_8:end
|
||||
|
||||
var box = new HBox(HGAP_20, btn, iconBtn);
|
||||
|
@ -8,7 +8,6 @@ import atlantafx.base.util.BBCodeParser;
|
||||
import atlantafx.sampler.page.ExampleBox;
|
||||
import atlantafx.sampler.page.OutlinePage;
|
||||
import atlantafx.sampler.page.Snippet;
|
||||
import atlantafx.sampler.theme.CSSFragment;
|
||||
import java.net.URI;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
@ -148,7 +147,7 @@ public final class CalendarPage extends OutlinePage {
|
||||
}
|
||||
|
||||
private ExampleBox styleExample() {
|
||||
var style = """
|
||||
var dataClass = """
|
||||
.date-picker-popup {
|
||||
-color-date-border: -color-accent-emphasis;
|
||||
-color-date-month-year-bg: -color-accent-emphasis;
|
||||
@ -161,7 +160,7 @@ public final class CalendarPage extends OutlinePage {
|
||||
// -color-date-border: -color-accent-emphasis;
|
||||
// -color-date-month-year-bg: -color-accent-emphasis;
|
||||
// -color-date-month-year-fg: -color-fg-emphasis;
|
||||
new CSSFragment(style).addTo(dp);
|
||||
dp.getStylesheets().add(Styles.toDataURI(dataClass));
|
||||
//snippet_4:end
|
||||
|
||||
var box = new HBox(dp);
|
||||
|
@ -5,11 +5,11 @@ package atlantafx.sampler.page.components;
|
||||
import atlantafx.base.controls.InlineDatePicker;
|
||||
import atlantafx.base.controls.Popover;
|
||||
import atlantafx.base.controls.Popover.ArrowLocation;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import atlantafx.base.util.BBCodeParser;
|
||||
import atlantafx.sampler.page.ExampleBox;
|
||||
import atlantafx.sampler.page.OutlinePage;
|
||||
import atlantafx.sampler.page.Snippet;
|
||||
import atlantafx.sampler.theme.CSSFragment;
|
||||
import java.net.URI;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
@ -51,7 +51,7 @@ public final class PopoverPage extends OutlinePage {
|
||||
}
|
||||
|
||||
private ExampleBox usageExample() {
|
||||
var datePickerStyle = """
|
||||
var dataClass = """
|
||||
.popover .date-picker-popup {
|
||||
-color-date-border: transparent;
|
||||
-color-date-bg: transparent;
|
||||
@ -84,7 +84,7 @@ public final class PopoverPage extends OutlinePage {
|
||||
// -color-date-day-bg: transparent;
|
||||
// -color-date-month-year-bg: transparent;
|
||||
// -color-date-day-bg-hover: -color-bg-subtle;
|
||||
new CSSFragment(datePickerStyle).addTo(datePicker);
|
||||
datePicker.getStylesheets().add(Styles.toDataURI(dataClass));
|
||||
|
||||
var pop2 = new Popover(datePicker);
|
||||
pop2.setHeaderAlwaysVisible(false);
|
||||
|
@ -8,7 +8,6 @@ import atlantafx.base.util.BBCodeParser;
|
||||
import atlantafx.sampler.page.ExampleBox;
|
||||
import atlantafx.sampler.page.OutlinePage;
|
||||
import atlantafx.sampler.page.Snippet;
|
||||
import atlantafx.sampler.theme.CSSFragment;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.geometry.HPos;
|
||||
@ -316,7 +315,7 @@ public final class ProgressIndicatorPage extends OutlinePage {
|
||||
// .example:danger .label {
|
||||
// -fx-text-fill: -color-fg-emphasis;
|
||||
// }
|
||||
new CSSFragment(dataClass).addTo(content);
|
||||
content.getStylesheets().add(Styles.toDataURI(dataClass));
|
||||
|
||||
bar.progressProperty().addListener((obs, old, val) -> {
|
||||
if (val == null) {
|
||||
|
@ -8,7 +8,6 @@ import atlantafx.base.util.BBCodeParser;
|
||||
import atlantafx.sampler.page.ExampleBox;
|
||||
import atlantafx.sampler.page.OutlinePage;
|
||||
import atlantafx.sampler.page.Snippet;
|
||||
import atlantafx.sampler.theme.CSSFragment;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -80,7 +79,7 @@ public final class IconsPage extends OutlinePage {
|
||||
}
|
||||
|
||||
private ExampleBox stackingExample() {
|
||||
var style1 = """
|
||||
var dataClass1 = """
|
||||
.stacked-ikonli-font-icon > .outer-icon {
|
||||
-fx-icon-size: 48px;
|
||||
-fx-icon-color: -color-danger-emphasis;
|
||||
@ -90,7 +89,7 @@ public final class IconsPage extends OutlinePage {
|
||||
}
|
||||
""";
|
||||
|
||||
var style2 = """
|
||||
var dataClass2 = """
|
||||
.stacked-ikonli-font-icon > .outer-icon {
|
||||
-fx-icon-size: 48px;
|
||||
}
|
||||
@ -115,7 +114,7 @@ public final class IconsPage extends OutlinePage {
|
||||
// .stacked-ikonli-font-icon > .inner-icon {
|
||||
// -fx-icon-size: 24px;
|
||||
// }
|
||||
new CSSFragment(style1).addTo(stackIcon1);
|
||||
stackIcon1.getStylesheets().add(Styles.toDataURI(dataClass1));
|
||||
|
||||
var outerIcon2 = new FontIcon(
|
||||
Material2OutlinedAL.CHECK_BOX_OUTLINE_BLANK
|
||||
@ -133,7 +132,7 @@ public final class IconsPage extends OutlinePage {
|
||||
// .stacked-ikonli-font-icon > .inner-icon {
|
||||
// -fx-icon-size: 24px;
|
||||
// }
|
||||
new CSSFragment(style2).addTo(stackIcon2);
|
||||
stackIcon2.getStylesheets().add(Styles.toDataURI(dataClass2));
|
||||
//snippet_2:end
|
||||
|
||||
var box = new HBox(HGAP_20, stackIcon1, stackIcon2);
|
||||
|
@ -1,61 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
package atlantafx.sampler.theme;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Objects;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
public final class CSSFragment {
|
||||
|
||||
private static final String DATA_URI_PREFIX = "data:base64,";
|
||||
|
||||
private final String css;
|
||||
|
||||
public CSSFragment(String css) {
|
||||
this.css = Objects.requireNonNull(css);
|
||||
}
|
||||
|
||||
public void addTo(Region region) {
|
||||
Objects.requireNonNull(region);
|
||||
region.getStylesheets().add(toDataURI());
|
||||
}
|
||||
|
||||
public void removeFrom(Region region) {
|
||||
Objects.requireNonNull(region);
|
||||
region.getStylesheets().remove(toDataURI());
|
||||
}
|
||||
|
||||
public boolean existsIn(Region region) {
|
||||
Objects.requireNonNull(region);
|
||||
return region.getStylesheets().contains(toDataURI());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
CSSFragment cssFragment = (CSSFragment) o;
|
||||
return css.equals(cssFragment.css);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(css);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return css;
|
||||
}
|
||||
|
||||
public String toDataURI() {
|
||||
return DATA_URI_PREFIX + new String(Base64.getEncoder().encode(css.getBytes(UTF_8)), UTF_8);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user