From 61e0284ce4735c8c590807898cfa8f1abb210590 Mon Sep 17 00:00:00 2001 From: leewyatt Date: Tue, 21 May 2024 01:10:46 +0900 Subject: [PATCH] Add util classes for enhanced string conversion and UI interactions Added classes in com.dlsc.gemsfx.util to provide enhanced formatting for Enums and improved UI interactions. The classes include EnumStringConverter, EnumUtil, SimpleStringConverter, and UIUtil which provide various helper methods for connecting UI and data models. It also includes corresponding demo classes to showcase the usage of the utilities. Additionally, enabled static dependency on org.jetbrains.annotations. --- .../demo/util/EnumStringConverterDemo.java | 77 +++++++ .../demo/util/SimpleStringConverterDemo.java | 74 +++++++ gemsfx-demo/src/main/java/module-info.java | 1 + gemsfx/pom.xml | 6 + .../dlsc/gemsfx/util/EnumStringConverter.java | 65 ++++++ .../java/com/dlsc/gemsfx/util/EnumUtil.java | 187 ++++++++++++++++ .../gemsfx/util/SimpleStringConverter.java | 99 +++++++++ .../java/com/dlsc/gemsfx/util/UIUtil.java | 204 ++++++++++++++++++ gemsfx/src/main/java/module-info.java | 2 + pom.xml | 7 + 10 files changed, 722 insertions(+) create mode 100644 gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/util/EnumStringConverterDemo.java create mode 100644 gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/util/SimpleStringConverterDemo.java create mode 100644 gemsfx/src/main/java/com/dlsc/gemsfx/util/EnumStringConverter.java create mode 100644 gemsfx/src/main/java/com/dlsc/gemsfx/util/EnumUtil.java create mode 100644 gemsfx/src/main/java/com/dlsc/gemsfx/util/SimpleStringConverter.java create mode 100644 gemsfx/src/main/java/com/dlsc/gemsfx/util/UIUtil.java diff --git a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/util/EnumStringConverterDemo.java b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/util/EnumStringConverterDemo.java new file mode 100644 index 00000000..b80243ae --- /dev/null +++ b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/util/EnumStringConverterDemo.java @@ -0,0 +1,77 @@ +package com.dlsc.gemsfx.demo.util; + +import com.dlsc.gemsfx.util.EnumStringConverter; +import javafx.application.Application; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; +import javafx.scene.text.Font; +import javafx.stage.Stage; + + +/** + * This class demonstrates the usage of EnumStringConverter, which is a specialized StringConverter implementation + * for Enum types. It provides a default mechanism to format Enum values in title case, replaces underscores with spaces, + * and capitalizes the first letter of each word. If the Enum value is null, it returns an empty string. + *

+ * {@link EnumStringConverter} + */ +public class EnumStringConverterDemo extends Application { + + public enum Status { + NOT_STARTED, + IN_PROGRESS, + COMPLETED, + CANCELLED + } + + @Override + public void start(Stage primaryStage) { + // Create a ComboBox and populate it with Status objects + ComboBox comboBox = new ComboBox<>(); + comboBox.getItems().addAll(Status.values()); + comboBox.getSelectionModel().selectFirst(); + + // The traditional way to set a StringConverter on a ComboBox + // comboBox.setConverter(new StringConverter() { + // @Override + // public String toString(Status object) { + // if (object != null) { + // return Arrays.stream(object.name().split("_")) + // .filter(word -> !word.isEmpty()) + // .map(word -> word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase()) + // .collect(Collectors.joining(" ")); + // } + // return ""; + // } + // + // @Override + // public Status fromString(String string) { + // return null; + // } + // }); + + // Set the converter to use EnumStringConverter with the default title case formatting + comboBox.setConverter(new EnumStringConverter<>()); + + // Create a VBox layout and add the ComboBox to it + Label label = new Label("Select a status:"); + label.setFont(Font.font(15)); + VBox vbox = new VBox(15, label, comboBox); + vbox.setAlignment(Pos.CENTER); + vbox.setPadding(new Insets(20)); + Scene scene = new Scene(vbox,300, 200); + + // Configure and show the primary stage + primaryStage.setTitle("EnumStringConverter Demo"); + primaryStage.setScene(scene); + primaryStage.show(); + } + + public static void main(String[] args) { + launch(args); + } +} diff --git a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/util/SimpleStringConverterDemo.java b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/util/SimpleStringConverterDemo.java new file mode 100644 index 00000000..b97b25e6 --- /dev/null +++ b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/util/SimpleStringConverterDemo.java @@ -0,0 +1,74 @@ +package com.dlsc.gemsfx.demo.util; + +import com.dlsc.gemsfx.util.SimpleStringConverter; +import javafx.application.Application; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; +import javafx.scene.text.Font; +import javafx.stage.Stage; + +import java.time.LocalDate; + +/** + * This class demonstrates the usage of SimpleStringConverter, which is a specialized StringConverter implementation + * that allows you to define a custom formatting function for the conversion of objects to strings. It also provides + * a default mechanism to handle null values and empty strings. + * {@link SimpleStringConverter} + */ +public class SimpleStringConverterDemo extends Application { + + public record Task(String description, LocalDate dueDate) { + } + + @Override + public void start(Stage primaryStage) { + // Create a ComboBox and populate it with Task objects + ComboBox comboBox = new ComboBox<>(); + comboBox.getItems().addAll( + new Task("Task 1", LocalDate.now().plusWeeks(3)), + new Task("Task 2", LocalDate.now().plusWeeks(5)), + new Task("Task 3", LocalDate.now().plusWeeks(6)) + ); + comboBox.getSelectionModel().selectFirst(); + + // The traditional way to set a StringConverter on a ComboBox + // comboBox.setConverter(new StringConverter() { + // @Override + // public String toString(Task object) { + // if (object != null) { + // return object.description() + " [Due: " + object.dueDate() +"]"; + // } + // return ""; + // } + // + // @Override + // public Task fromString(String string) { + // return null; + // } + // }); + + // Set the converter to use SimpleStringConverter with the default title case formatting + comboBox.setConverter(new SimpleStringConverter<>(task -> task.description() + " [Due: " + task.dueDate() + "]", "")); + + // Create a VBox layout and add the ComboBox to it + Label label = new Label("Select a status:"); + label.setFont(new Font(16)); + VBox vbox = new VBox(15, label, comboBox); + vbox.setPadding(new Insets(15)); + vbox.setAlignment(Pos.CENTER); + Scene scene = new Scene(vbox, 300, 200); + + // Configure and show the primary stage + primaryStage.setTitle("SimpleStringConverter Demo"); + primaryStage.setScene(scene); + primaryStage.show(); + } + + public static void main(String[] args) { + launch(args); + } +} diff --git a/gemsfx-demo/src/main/java/module-info.java b/gemsfx-demo/src/main/java/module-info.java index 67a6fd6f..1a712f5f 100644 --- a/gemsfx-demo/src/main/java/module-info.java +++ b/gemsfx-demo/src/main/java/module-info.java @@ -19,4 +19,5 @@ exports com.dlsc.gemsfx.demo; exports com.dlsc.gemsfx.demo.binding; + exports com.dlsc.gemsfx.demo.util; } \ No newline at end of file diff --git a/gemsfx/pom.xml b/gemsfx/pom.xml index 108945d5..fa9a578d 100644 --- a/gemsfx/pom.xml +++ b/gemsfx/pom.xml @@ -46,6 +46,12 @@ + + org.jetbrains + annotations + true + + net.synedra validatorfx diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/util/EnumStringConverter.java b/gemsfx/src/main/java/com/dlsc/gemsfx/util/EnumStringConverter.java new file mode 100644 index 00000000..fa864e56 --- /dev/null +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/util/EnumStringConverter.java @@ -0,0 +1,65 @@ +package com.dlsc.gemsfx.util; + +import javafx.util.Callback; + +/** + * A specialized StringConverter implementation for Enum types. + * This converter provides a default mechanism to format Enum values + * in a title case, replacing underscores with spaces and capitalizing + * the first letter of each word. If the enum value is null, it returns an empty string. + * + *

+ * This class extends SimpleStringConverter and leverages EnumUtil to provide + * a default formatting for enum values. It can also accept custom callbacks for + * more specific conversion requirements. + *

+ * + *

Usage Example:

+ *

Default usage with title case formatting:

+ *
{@code
+ * ComboBox comboBox = new ComboBox<>();
+ * comboBox.setConverter(new EnumStringConverter<>());
+ * }
+ * + *

Custom callback for specific formatting:

+ *
{@code
+ * ComboBox comboBox = new ComboBox<>();
+ * comboBox.setConverter(new EnumStringConverter<>(myEnum -> "Custom format: " + myEnum.name()));
+ * }
+ * + * @param the type of the Enum to be converted. + */ +public class EnumStringConverter> extends SimpleStringConverter { + + /** + * Converts an enum value to title case, replacing underscores with spaces and + * capitalizing the first letter of each word. If the enum value is null, returns an empty string. + *

Example: 1. null -> "" + *

Example: 2. MY_ENUM_VALUE -> My Enum Value + */ + public EnumStringConverter() { + this(EnumUtil::formatEnumNameAsTitleCase); + } + + /** + * Constructor that accepts a custom callback for converting enum values to strings. + * The provided callback should handle conversion from enum value to String, including any necessary null handling. + * + * @param valueToStringCallback The callback to convert enum value to a String. + */ + public EnumStringConverter(Callback valueToStringCallback) { + super(valueToStringCallback); + } + + /** + * Constructor that accepts a custom callback for converting non-null enum values to strings and a default string + * to return if the enum value is null. + * + * @param nonNullValueCallback The callback to convert non-null enum value to a String. + * @param nullDefaultValue The default String value to return if the enum value is null. + */ + public EnumStringConverter(Callback nonNullValueCallback, String nullDefaultValue) { + super(nonNullValueCallback, nullDefaultValue); + } + +} \ No newline at end of file diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/util/EnumUtil.java b/gemsfx/src/main/java/com/dlsc/gemsfx/util/EnumUtil.java new file mode 100644 index 00000000..82cde653 --- /dev/null +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/util/EnumUtil.java @@ -0,0 +1,187 @@ +package com.dlsc.gemsfx.util; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Utility class for working with enums. + *

formatEnumNameAsCapitalized: MY_ENUM_VALUE -> My enum value + *

formatEnumNameAsTitleCase: MY_ENUM_VALUE -> My Enum Value + *

formatEnumNameAsSpacedWords: MY_ENUM_VALUE -> MY ENUM VALUE + *

convertToStyleClassName: MY_ENUM_VALUE -> my-enum-value + */ +public class EnumUtil { + + private EnumUtil() { + } + + /** + * Converts a string representation of an enum name to a capitalized format, + * replacing underscores with spaces and making the rest lowercase. + * If the input string is null, returns an empty string. + * + * @param enumValue The enum value to convert. + * @return A capitalized string representation of the enum name. + *

Example: 1. null -> "" + *

Example: 2. MY_ENUM_VALUE -> My enum value + */ + public static > String formatEnumNameAsCapitalized(@Nullable T enumValue) { + return formatEnumNameAsCapitalized(enumValue, ""); + } + + /** + * Converts a string representation of an enum name to a capitalized format, + * replacing underscores with spaces and making the rest lowercase. + * If the input string is null, returns the specified default value. + * + * @param enumValue The enum value to convert. + * @param nullDefaultValue The default value to return if the input is null. + * @return A capitalized string representation of the enum name. + *

Example: 1. null -> nullDefaultValue + *

Example: 2. Example: MY_ENUM_VALUE -> My enum value + */ + public static > String formatEnumNameAsCapitalized(@Nullable T enumValue, @Nullable String nullDefaultValue) { + return enumValue == null ? nullDefaultValue : formatEnumNameAsCapitalized(enumValue.name()); + } + + /** + * Converts a string representation of an enum name to a capitalized format, + * replacing underscores with spaces and making the rest lowercase. + * If the input string is null, returns an empty string. + * + * @param enumName The enum name to convert. + * @return A capitalized string representation of the enum name. + *

Example: 1. null -> "" + *

Example: 2. Example: MY_ENUM_VALUE -> My enum value + */ + public static > String formatEnumNameAsCapitalized(@Nullable String enumName) { + return enumName == null ? "" : StringUtils.capitalize(enumName.replace("_", " ").toLowerCase()); + } + + /** + * Converts an enum value to title case, replacing underscores with spaces and + * capitalizing the first letter of each word. If the enum value is null, returns an empty string. + * + * @param enumValue The enum value to be formatted. + * @return A title-cased string representation of the enum value. + *

Example: 1. null -> "" + *

Example: 2. MY_ENUM_VALUE -> My Enum Value + */ + public static > String formatEnumNameAsTitleCase(@Nullable T enumValue) { + return formatEnumNameAsTitleCase(enumValue, ""); + } + + /** + * Converts an enum value to title case, replacing underscores with spaces and + * capitalizing the first letter of each word. If the enum value is null, returns the specified default value. + * + * @param enumValue The enum value to be formatted. + * @param nullDefaultValue The default value to return if the input is null. + * @return A title-cased string representation of the enum value. + *

Example: 1. null -> nullDefaultValue + *

Example: 2. MY_ENUM_VALUE -> My Enum Value + */ + public static > String formatEnumNameAsTitleCase(@Nullable T enumValue, @Nullable String nullDefaultValue) { + return formatEnumNameAsTitleCase(enumValue == null ? null : enumValue.name(), nullDefaultValue); + } + + /** + * Converts an enum value to title case, replacing underscores with spaces and + * capitalizing the first letter of each word. If the enum value is null, returns an empty string. + * + * @param enumName The string representation of the enum value to be formatted. + * @return A title-cased string representation of the enum value. + *

Example: 1. null -> "" + *

Example: 2. MY_ENUM_VALUE -> My Enum Value + */ + public static > String formatEnumNameAsTitleCase(@Nullable String enumName) { + return formatEnumNameAsTitleCase(enumName, ""); + } + + /** + * Converts an enum value to title case, replacing underscores with spaces and + * capitalizing the first letter of each word. If the enum value is null, returns the specified default value. + * + * @param enumName The string representation of the enum value to be formatted. + * @param nullDefaultValue The default value to return if the input is null. + * @return A title-cased string representation of the enum value. + *

Example: 1. null -> nullDefaultValue + *

Example: 2. MY_ENUM_VALUE -> My Enum Value + */ + public static > String formatEnumNameAsTitleCase(@Nullable String enumName, @Nullable String nullDefaultValue) { + if (enumName == null) { + return nullDefaultValue; + } + + // Replace underscores with spaces and convert to lower case + String lowerCased = enumName.replace("_", " ").toLowerCase(); + + // Split the string into words + String[] words = StringUtils.split(lowerCased); + + // Use StringBuilder to build the final string + StringBuilder result = new StringBuilder(); + for (String word : words) { + if (!result.isEmpty()) { + result.append(" "); + } + // Capitalize the first letter of each word and append to result + result.append(StringUtils.capitalize(word)); + } + + return result.toString(); + } + + /** + * Replaces underscores in the string representation of an enum name with spaces. + * Does not change letter case. If the input string is null, returns an empty string. + * + * @param enumValue The enum value to be formatted. + * @return A string representation of the enum name with underscores replaced by spaces. + *

Example: 1. null -> "" + *

Example: 2. MY_ENUM_VALUE -> MY ENUM VALUE + */ + public static > String formatEnumNameAsSpacedWords(@Nullable T enumValue) { + return enumValue == null ? "" : enumValue.name().replace("_", " "); + } + + /** + * Replaces underscores in the string representation of an enum name with spaces. + * Does not change letter case. If the input string is null, returns an empty string. + * + * @param enumName The string representation of the enum name to be formatted. + * @return A string representation of the enum name with underscores replaced by spaces. + *

Example: 1. null -> "" + *

Example: 2. MY_ENUM_VALUE -> MY ENUM VALUE + */ + public static > String formatEnumNameAsSpacedWords(@Nullable String enumName) { + return enumName == null ? "" : enumName.replace("_", " "); + } + + /** + * Converts the enum name to a lower case string, replacing underscores with hyphens, + * suitable for use as a CSS class name. This method does not accept null values for enumValue. + * + * @param enumValue The enum value to be converted. Must not be null. + * @return A string suitable for use as a CSS class name. + * Example: MY_ENUM_VALUE -> my-enum-value + */ + public static > String convertToStyleClassName(@NotNull T enumValue) { + return enumValue.name().toLowerCase().replace("_", "-"); + } + + public static > String[] convertAllToStylesClassName(@NotNull Class enumClass) { + T[] enumConstants = enumClass.getEnumConstants(); + return convertAllToStylesClassName(enumConstants); + } + + public static > String[] convertAllToStylesClassName(@NotNull T[] enumValues) { + String[] styles = new String[enumValues.length]; + for (int i = 0; i < enumValues.length; i++) { + styles[i] = convertToStyleClassName(enumValues[i]); + } + return styles; + } + +} diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/util/SimpleStringConverter.java b/gemsfx/src/main/java/com/dlsc/gemsfx/util/SimpleStringConverter.java new file mode 100644 index 00000000..338858e2 --- /dev/null +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/util/SimpleStringConverter.java @@ -0,0 +1,99 @@ +package com.dlsc.gemsfx.util; + +import javafx.util.Callback; +import javafx.util.StringConverter; + +import java.util.Optional; + +/** + * A generic StringConverter implementation primarily used for displaying objects in the UI. + * This class provides flexible mechanisms for converting objects to Strings using custom callbacks. + * It also supports handling null values by returning a default string for null values. + * + *

+ * This converter is typically used to format objects as strings for display purposes, with optional + * handling for null values. The conversion from string back to object is not usually required, + * hence the {@link #fromString(String)} method returns null. + *

+ * + *

Usage Example:

+ *

Before using SimpleStringConverter:

+ *
{@code
+ * comboBox.setConverter(new StringConverter<>() {
+ *     @Override
+ *     public String toString(Status status) {
+ *         if (status != null) {
+ *             return status.getDescription();
+ *         }
+ *         return "";
+ *     }
+ *
+ *     @Override
+ *     public Status fromString(String string) {
+ *         return null;
+ *     }
+ * });
+ * }
+ * + *

After using SimpleStringConverter:

+ *
{@code
+ * comboBox.setConverter(new SimpleStringConverter<>(Status::getDescription, ""));
+ * }
+ * + * @param the type of the object to be converted. + */ +public class SimpleStringConverter extends StringConverter { + private final Callback valueToStringCallback; + + public SimpleStringConverter() { + this(Object::toString, ""); + } + + /** + * Constructor that requires callers to handle null values themselves. + * The provided callback should handle conversion from value to String, + * including any necessary null handling. + * + * @param valueToStringCallback The callback to convert value to a String. + */ + public SimpleStringConverter(Callback valueToStringCallback) { + this.valueToStringCallback = valueToStringCallback; + } + + /** + * Constructor that automatically handles null values by returning a default null value string. + * If the value is null, the specified nullDefaultValue is returned instead of throwing an error or returning null. + * + * @param nonNullValueCallback The callback to convert non-null value to a String. + * @param nullDefaultValue The default String value to return if the value is null. + */ + public SimpleStringConverter(Callback nonNullValueCallback, String nullDefaultValue) { + this.valueToStringCallback = value -> Optional.ofNullable(value) + .map(nonNullValueCallback::call) + .orElse(nullDefaultValue); + } + + @Override + public String toString(T object) { + if (this.valueToStringCallback != null && object != null) { + return this.valueToStringCallback.call(object); + } + return ""; + } + + /** + * This method is not implemented and always returns null. + * + *

+ * Since the primary use of this converter is to display objects as strings in the UI, + * converting strings back to objects is not required. Therefore, this method simply returns null. + *

+ * + * @param s the string to be converted to an object. + * @return always returns null. + */ + @Override + public T fromString(String s) { + return null; + } +} diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/util/UIUtil.java b/gemsfx/src/main/java/com/dlsc/gemsfx/util/UIUtil.java new file mode 100644 index 00000000..b6818d7d --- /dev/null +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/util/UIUtil.java @@ -0,0 +1,204 @@ +package com.dlsc.gemsfx.util; + + +import javafx.geometry.Insets; +import javafx.scene.Node; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class UIUtil { + + private UIUtil() { + } + + /** + * Adds a style class to a node if it is not already present. + * + * @param node The node to add the style class to. + * @param styleClass The style class to add. + */ + public static void addClassIfAbsent(@Nullable Node node, @NotNull String styleClass) { + Optional.ofNullable(node).ifPresent(n -> { + if (!n.getStyleClass().contains(styleClass)) { + n.getStyleClass().add(styleClass); + } + }); + } + + /** + * Adds a list of style classes to a node if they are not already present. + * + * @param node The node to add the style classes to. + * @param styleClasses The style classes to add. + */ + public static void addClassesIfAbsent(@NotNull Node node, @NotNull String... styleClasses) { + List list = Arrays.stream(styleClasses) + .filter(styleClass -> !node.getStyleClass().contains(styleClass)) + .toList(); + node.getStyleClass().addAll(list); + } + + /** + * Toggles a style class on a node. + * If the style class is present, it is removed. + * If it is not present, it is added. + * + * @param node The node to toggle the style on. + * @param styleClass The style class to add or remove. + */ + public static void toggleClass(@NotNull Node node, @NotNull String styleClass) { + if (node.getStyleClass().contains(styleClass)) { + node.getStyleClass().remove(styleClass); + } else { + node.getStyleClass().add(styleClass); + } + } + + /** + * Toggles a style class on a node based on a condition. + * If the condition is true, the style class is added. + * If the condition is false, the style class is removed. + * + * @param node The node to toggle the style on. + * @param styleClass The style class to add or remove. + * @param condition The condition that determines whether to add or remove the style. + */ + public static void toggleClassOnCondition(@NotNull Node node, @NotNull String styleClass, boolean condition) { + if (condition) { + addClassIfAbsent(node, styleClass); + } else { + node.getStyleClass().remove(styleClass); + } + } + + + /** + * Optimizes style updates for a given node by first adding a specified style to ensure it's present, + * and then removing other specified styles, except the newly added one. This approach helps in preventing + * unnecessary UI flicker by avoiding the removal of a style that needs to be present. + * + * @param node The node whose styles are to be updated. + * @param stylesToRemove A list of styles to be removed from the node, except for the styleToAdd. + * @param styleToAdd The style to be added to the node, if it's not already present. + */ + public static void updateStyles(@NotNull Node node, @NotNull List stylesToRemove, @NotNull String styleToAdd) { + // Add the style if it's not already present + addClassIfAbsent(node, styleToAdd); + + // Remove the specified styles except the style to be added + node.getStyleClass().removeAll(stylesToRemove.stream() + .filter(style -> !style.equals(styleToAdd)) + .toList()); + } + + /** + * Optimizes style updates for a given node by first adding a specified style to ensure it's present, + * and then removing other specified styles, except the newly added one. This approach helps in preventing + * unnecessary UI flicker by avoiding the removal of a style that needs to be present. + * + * @param node The node whose styles are to be updated. + * @param stylesToRemove An array of styles to be removed from the node, except for the styleToAdd. + * @param styleToAdd The style to be added to the node, if it's not already present. + */ + public static void updateStyles(@NotNull Node node, @NotNull String[] stylesToRemove, @NotNull String styleToAdd) { + updateStyles(node, Arrays.asList(stylesToRemove), styleToAdd); + } + + /** + * Applies a style derived from an enum value to a Node, removing other styles from the same enum class. + * + * @param node The Node to update. + * @param enumValue The enum value determining the style to apply. + *

Example If Dir.UP is passed, add "up" style and removes {"down", "left", "right"} styles. + */ + public static > void updateStyleFromEnum(@NotNull Node node, @NotNull T enumValue) { + updateStyles(node, EnumUtil.convertAllToStylesClassName(enumValue.getClass()), EnumUtil.convertToStyleClassName(enumValue)); + } + + /** + * Removes all styles associated with a given enum class from a Node. + * + * @param node The Node to clear styles from. + * @param enumClass The enum class whose associated styles will be removed. + *

Example If Dir.class is passed, removes all styles {"up","down","left", "right"}. + */ + public static > void clearStylesByEnum(@NotNull Node node, @NotNull Class enumClass) { + node.getStyleClass().removeAll(EnumUtil.convertAllToStylesClassName(enumClass)); + } + + /** + * Returns the height of the top and bottom insets combined. + */ + public static double getInsetsHeight(@Nullable Insets insets) { + return insets == null ? 0 : insets.getTop() + insets.getBottom(); + } + + /** + * Returns the width of the left and right insets combined. + */ + public static double getInsetsWidth(@Nullable Insets insets) { + return insets == null ? 0 : insets.getLeft() + insets.getRight(); + } + + /** + * Converts a camelCase string to a natural language expression. + *

+ * This method is designed to transform strings formatted in camelCase, + * commonly used in programming, into a more readable format that is + * suitable for display in user interfaces. It inserts spaces between + * words and ensures that acronyms remain capitalized while the first + * letter of the resulting string is also capitalized. + *

+ *

+ * Example: + * "ONSCode" becomes "ONS Code" + * "customerServiceOrder" becomes "Customer Service Order" + *

+ * + * @param camelCaseString The camelCase string to be converted. + * @return A string in natural language format, with appropriate spaces and capitalization. + */ + public static String camelCaseToNaturalCase(@NotNull String camelCaseString) { + return StringUtils.capitalize(StringUtils.join(StringUtils.splitByCharacterTypeCamelCase(camelCaseString), " ")); + } + + /** + * Copies the specified content to the clipboard. + * + * @param copyContent The content to be copied to the clipboard. + */ + public static void copyToClipboard(String copyContent) { + ClipboardContent content = new ClipboardContent(); + content.putString(copyContent); + Clipboard.getSystemClipboard().setContent(content); + } + + /** + * Determines if the given mouse event is a single primary button click + * that hasn't moved since it was pressed. + * + *

This method checks if the mouse event satisfies the following conditions: + *

    + *
  • The mouse button is the primary button (usually the left button).
  • + *
  • The click count is exactly one.
  • + *
  • The mouse has not moved since it was pressed.
  • + *
+ * + * @param event The mouse event to check. Must not be null. + * @return {@code true} if the event is a single stable primary button click, {@code false} otherwise. + * @throws NullPointerException if the event is null. + */ + public static boolean clickOnNode(@NotNull MouseEvent event) { + return event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 1 && event.isStillSincePress(); + } + +} \ No newline at end of file diff --git a/gemsfx/src/main/java/module-info.java b/gemsfx/src/main/java/module-info.java index d7fd49df..69932851 100644 --- a/gemsfx/src/main/java/module-info.java +++ b/gemsfx/src/main/java/module-info.java @@ -26,6 +26,8 @@ requires com.dlsc.pickerfx; requires com.dlsc.unitfx; + requires static org.jetbrains.annotations; + exports com.dlsc.gemsfx; exports com.dlsc.gemsfx.daterange; exports com.dlsc.gemsfx.incubator; diff --git a/pom.xml b/pom.xml index 511abe14..74cf8866 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,13 @@ + + org.jetbrains + annotations + 24.1.0 + true + + org.controlsfx controlsfx