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/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..9e997223 --- /dev/null +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/util/EnumUtil.java @@ -0,0 +1,185 @@ +package com.dlsc.gemsfx.util; + +import org.apache.commons.lang3.StringUtils; + +/** + * 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(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(T enumValue, 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(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(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(T enumValue, 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(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(String enumName, 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(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(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(T enumValue) { + return enumValue.name().toLowerCase().replace("_", "-"); + } + + public static > String[] convertAllToStylesClassName(Class enumClass) { + T[] enumConstants = enumClass.getEnumConstants(); + return convertAllToStylesClassName(enumConstants); + } + + public static > String[] convertAllToStylesClassName(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..f4247575 --- /dev/null +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/util/UIUtil.java @@ -0,0 +1,202 @@ +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 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(Node node, 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(Node node, 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(Node node, 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(Node node, 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(Node node, List stylesToRemove, 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(Node node, String[] stylesToRemove, 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(Node node, 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(Node node, Class enumClass) { + node.getStyleClass().removeAll(EnumUtil.convertAllToStylesClassName(enumClass)); + } + + /** + * Returns the height of the top and bottom insets combined. + */ + public static double getInsetsHeight(Insets insets) { + return insets == null ? 0 : insets.getTop() + insets.getBottom(); + } + + /** + * Returns the width of the left and right insets combined. + */ + public static double getInsetsWidth(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(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(MouseEvent event) { + return event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 1 && event.isStillSincePress(); + } + +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 511abe14..64a2f564 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,7 @@ + org.controlsfx controlsfx