diff --git a/.editorconfig b/.editorconfig index af32f6166f35dbd20f67251378e3b9cd2e383b1a..ba60bec869a65c74991807aa96d724ec3aafdf45 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,30 @@ -[*] +root = true + +[*.{txt, java, xml, md, properties}] charset = utf-8 -end_of_line = lf -insert_final_newline = false indent_style = space +end_of_line = lf + +[*.{txt, java, xml}] indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false + +[*.java] +ij_java_blank_lines_around_field = 1 +ij_java_blank_lines_after_class_header = 1 +ij_java_doc_add_blank_line_after_return = true +ij_java_doc_add_blank_line_after_param_comments = true +ij_java_doc_add_blank_line_after_description = true +ij_java_doc_keep_empty_parameter_tag = false +ij_java_doc_keep_empty_return_tag = false +ij_java_doc_keep_empty_throws_tag = false + +[*.properties] +ij_properties_keep_blank_lines = true +ij_properties_spaces_around_key_value_delimiter = true -[*.{html, xml, yaml}] -indent_size = 2 diff --git a/.gitignore b/.gitignore index 29ed5a82c2fba4c716ea5d61a9ff05fbaa6bc9ae..46fd667c245a3eebfd44d031b0b7ee78b1bc4df4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ # Default ignored files #/.gitignore -/xcore.iml -/data -/log/ -/target/ -/.idea \ No newline at end of file +data +log/ +target/ +.idea/ +.mvnd/ +*.iml + +C: \ No newline at end of file diff --git a/README.md b/README.md index 620c533bb4beb9906f055932b4df02a6c1530b07..be324376089795b6943bb5e7005c98a0377cc36c 100644 --- a/README.md +++ b/README.md @@ -15,16 +15,16 @@ ``` - xwintop-maven - https://xwintop.gitee.io/maven/repository + jitpack.io + https://jitpack.io - com.xwintop + com.gitee.xwintop xcore - 0.0.3-SNAPSHOT + 0.0.7 provided @@ -43,4 +43,10 @@ git 托管 maven可参考教程(若无法下载请拉取项目自行编译)。[ - 0.0.4-SNAPSHOT 2020-03-16 1. 优化界面布局 - 0.0.5 2020-03-16 - 1. 添加进度条对话框 \ No newline at end of file + 1. 添加进度条对话框 +- 0.0.6 2020-07-04 + 1. 添加历史输入框功能封装 + 2. 优化部分功能代码 +- 0.0.7 2022-03-29 + 1. 升级jdk版本为17 + 2. 优化部分功能代码 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5ebf01104d390705136982396ba9e3df89c225b8..a739f158c7580cf1e60aa784c743d76f0fa88558 100644 --- a/pom.xml +++ b/pom.xml @@ -1,205 +1,243 @@ - 4.0.0 - - com.xwintop - xcore - 0.0.6 - jar - - xcore - xcore核心jar包 - https://gitee.com/xwintop/xcore - - - 1.8 - 1.8 - UTF-8 - - - - - aliyunmaven - http://maven.aliyun.com/nexus/content/groups/public/ - - - spring-snapshots - http://repo.spring.io/snapshot - - true - - - - spring-milestones - http://repo.spring.io/milestone - - - apache.snapshots - Apache Development Snapshot Repository - https://repository.apache.org/content/repositories/snapshots/ - - false - - - true - - - - - - - junit - junit - 4.12 - test - - - - ch.qos.logback - logback-classic - 1.2.3 - - - - - org.dom4j - dom4j - 2.1.1 - - - jaxen - jaxen - 1.2.0 - - - - - org.quartz-scheduler - quartz - 2.3.1 - - - - - org.controlsfx - controlsfx - 8.40.16 - - - - com.jfoenix - jfoenix - 8.0.9 - - - - - commons-codec - commons-codec - 1.12 - - - commons-configuration - commons-configuration - 1.10 - - - commons-io - commons-io - 2.6 - - - org.apache.commons - commons-lang3 - 3.9 - - - org.apache.commons - commons-collections4 - 4.4 - - - org.apache.commons - commons-text - 1.8 - - - commons-beanutils - commons-beanutils - 1.9.3 - - - org.apache.commons - commons-imaging - 1.0-alpha1 - - - - - com.squareup.okhttp3 - okhttp - 4.5.0 - - - - - org.projectlombok - lombok - 1.18.6 - provided - - - - - cn.hutool - hutool-all - 5.3.1 - - - - - com.alibaba - fastjson - 1.2.68 - - - - - org.yaml - snakeyaml - 1.26 - - - - - - - xwintop-maven - file:C:/gitFiles/maven/repository/ - - - - - - - - org.apache.maven.plugins - maven-resources-plugin - 3.0.2 - - UTF-8 - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.20 - - true - once - -Dfile.encoding=UTF-8 - - - - + 4.0.0 + + com.gitee.xwintop + xcore + 0.0.7 + jar + + xcore + xcore核心jar包 + https://gitee.com/xwintop/xcore + + + 17 + 17 + UTF-8 + true + 17.0.2 + + + + + aliyunmaven + https://maven.aliyun.com/repository/public + + + + + + + + + + org.openjfx + javafx-controls + ${javafx.version} + + + org.openjfx + javafx-base + ${javafx.version} + + + org.openjfx + javafx-graphics + ${javafx.version} + + + org.openjfx + javafx-fxml + ${javafx.version} + + + org.openjfx + javafx-swing + ${javafx.version} + + + org.openjfx + javafx-media + ${javafx.version} + + + org.openjfx + javafx-web + ${javafx.version} + + + + junit + junit + 4.13.2 + test + + + + ch.qos.logback + logback-classic + 1.2.11 + + + + + org.dom4j + dom4j + 2.1.3 + + + jaxen + jaxen + 1.2.0 + + + + + org.quartz-scheduler + quartz + 2.3.2 + + + + + org.controlsfx + controlsfx + 11.1.1 + + + + com.jfoenix + jfoenix + 9.0.10 + + + + + + + + + + + + + + + + + + commons-codec + commons-codec + 1.15 + + + commons-configuration + commons-configuration + 1.10 + + + commons-io + commons-io + 2.11.0 + + + org.apache.commons + commons-lang3 + 3.12.0 + + + org.apache.commons + commons-collections4 + 4.4 + + + org.apache.commons + commons-text + 1.9 + + + commons-beanutils + commons-beanutils + 1.9.4 + + + org.apache.commons + commons-imaging + 1.0-alpha2 + + + + + com.squareup.okhttp3 + okhttp + 4.9.3 + + + + + org.projectlombok + lombok + 1.18.22 + provided + + + + + cn.hutool + hutool-all + 5.7.22 + + + + + com.alibaba + fastjson + 1.2.80 + + + + + org.yaml + snakeyaml + 1.30 + + + + + + + xwintop-maven + file:C:/gitFiles/maven/repository/ + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.2.0 + + UTF-8 + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + true + once + -Dfile.encoding=UTF-8 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.0 + + + diff --git a/src/main/java/com/xwintop/xcore/info/package-info.java b/src/main/java/com/xwintop/xcore/info/package-info.java deleted file mode 100644 index b501c9b37dc97a1f207435b86a2a6a702022c4bc..0000000000000000000000000000000000000000 --- a/src/main/java/com/xwintop/xcore/info/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - * - */ -/** - * @author Administrator - * - */ -package com.xwintop.xcore.info; \ No newline at end of file diff --git a/src/main/java/com/xwintop/xcore/javafx/cells/ListCellFactory.java b/src/main/java/com/xwintop/xcore/javafx/cells/ListCellFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..b7cc5ec944659f7afedfd7ce9fb1254d8ca59a44 --- /dev/null +++ b/src/main/java/com/xwintop/xcore/javafx/cells/ListCellFactory.java @@ -0,0 +1,135 @@ +package com.xwintop.xcore.javafx.cells; + +import javafx.beans.value.ObservableValue; +import javafx.scene.Node; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.util.Callback; + +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * 封装对列表节点的配置 + * + * @author yiding.he + */ +public class ListCellFactory implements Callback, ListCell> { + + private Function textFunction; + + private Function> textProperty; + + private Function graphicFunction; + + private Function> graphicProperty; + + private Consumer> cellInitializer; + + /** + * 对 ListCell 的额外配置 + */ + public ListCellFactory setCellInitializer(Consumer> cellInitializer) { + this.cellInitializer = cellInitializer; + return this; + } + + /** + * 设置列表项文本 + */ + public ListCellFactory withTextFunction(Function toStringFunction) { + if (this.textProperty != null) { + throw new IllegalStateException("You have already assigned textProperty."); + } + this.textFunction = toStringFunction; + return this; + } + + /** + * 绑定列表项文本为一个自动更新的属性 + */ + public ListCellFactory withTextProperty(Function> toStringProperty) { + if (this.textFunction != null) { + throw new IllegalStateException("You have already assigned textFunction."); + } + this.textProperty = toStringProperty; + return this; + } + + /** + * 设置列表项图标 + */ + public ListCellFactory withGraphicFunction(Function graphicFunction) { + if (this.graphicProperty != null) { + throw new IllegalStateException("You have already assigned graphicProperty."); + } + this.graphicFunction = graphicFunction; + return this; + } + + /** + * 绑定列表项图标为一个自动更新的属性 + */ + public ListCellFactory withGraphicProperty(Function> graphicProperty) { + if (this.graphicFunction != null) { + throw new IllegalStateException("You have already assigned graphicFunction."); + } + this.graphicProperty = graphicProperty; + return this; + } + + @Override + public ListCell call(ListView param) { + + ListCell listCell = new ListCell() { + @Override + protected void updateItem(T item, boolean empty) { + super.updateItem(item, empty); + + if (textProperty().isBound()) { + textProperty().unbind(); + } + if (graphicProperty().isBound()) { + graphicProperty().unbind(); + } + + if (empty) { + setText(null); + setGraphic(null); + } else { + setCellText(this, item); + setCellGraphic(this, item); + } + } + }; + + if (cellInitializer != null) { + cellInitializer.accept(listCell); + } + + return listCell; + } + + private void setCellGraphic(ListCell cell, T item) { + if (graphicFunction != null) { + cell.setGraphic(graphicFunction.apply(item)); + } + if (graphicProperty != null) { + cell.graphicProperty().unbind(); + cell.graphicProperty().bind(graphicProperty.apply(item)); + } + } + + private void setCellText(ListCell cell, T item) { + if (textFunction != null) { + cell.setText(textFunction.apply(item)); + } + if (textProperty != null) { + cell.textProperty().unbind(); + cell.textProperty().bind(textProperty.apply(item)); + } + if (textFunction == null && textProperty == null) { + cell.setText(String.valueOf(item)); + } + } +} diff --git a/src/main/java/com/xwintop/xcore/javafx/cells/TreeCellFactory.java b/src/main/java/com/xwintop/xcore/javafx/cells/TreeCellFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..875527291393a215e015dc96cc2e9bc7c711e1f4 --- /dev/null +++ b/src/main/java/com/xwintop/xcore/javafx/cells/TreeCellFactory.java @@ -0,0 +1,91 @@ +package com.xwintop.xcore.javafx.cells; + +import javafx.scene.control.TreeCell; +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeView; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.util.Callback; + +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * 封装对 TreeView 的配置过程 + * + * @author yiding.he + */ +public class TreeCellFactory implements Callback, TreeCell> { + + private Consumer onDoubleClick; + + private Function toString; + + private Function, Image> iconSupplier; + + /** + * 设置当双击树节点时要做的事 + */ + public TreeCellFactory setOnDoubleClick(Consumer onDoubleClick) { + this.onDoubleClick = onDoubleClick; + return this; + } + + /** + * 设置树节点文本 + */ + public TreeCellFactory setToString(Function toString) { + this.toString = toString; + return this; + } + + /** + * 设置树节点图标 + */ + public TreeCellFactory setIconSupplier(Function, Image> iconSupplier) { + this.iconSupplier = iconSupplier; + return this; + } + + @Override + public TreeCell call(TreeView param) { + TreeCell treeCell = new TreeCell() { + @Override + protected void updateItem(T item, boolean empty) { + super.updateItem(item, empty); + + if (empty) { + setText(null); + setGraphic(null); + } else { + setCellText(item); + setCellIcon(this.getTreeItem()); + } + } + + private void setCellIcon(TreeItem treeItem) { + if (iconSupplier != null) { + Image image = iconSupplier.apply(treeItem); + ImageView imageView = new ImageView(image); + imageView.setFitWidth(16); + imageView.setFitHeight(16); + setGraphic(imageView); + } + } + + private void setCellText(T item) { + setText(toString != null ? toString.apply(item) : String.valueOf(item)); + } + }; + + if (onDoubleClick != null) { + treeCell.setOnMouseClicked(event -> { + if (!treeCell.isEmpty() && event.getClickCount() == 2) { + onDoubleClick.accept(treeCell.getItem()); + } + }); + } + + return treeCell; + } +} diff --git a/src/main/java/com/xwintop/xcore/javafx/decorator/ComboBoxDecorator.java b/src/main/java/com/xwintop/xcore/javafx/decorator/ComboBoxDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..d9d72a2cb2e55c8b3cde3df52b54f520e8a90303 --- /dev/null +++ b/src/main/java/com/xwintop/xcore/javafx/decorator/ComboBoxDecorator.java @@ -0,0 +1,103 @@ +package com.xwintop.xcore.javafx.decorator; + +import com.xwintop.xcore.javafx.cells.ListCellFactory; +import javafx.scene.control.ComboBox; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.cell.ComboBoxListCell; +import javafx.util.Callback; +import javafx.util.StringConverter; + +import java.util.Collection; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * 封装对 ComboBox 的配置 + */ +@SuppressWarnings("unchecked") +public class ComboBoxDecorator { + + public static ComboBoxDecorator of(ComboBox comboBox) { + return new ComboBoxDecorator<>(comboBox); + } + + private ComboBox comboBox; + + private ComboBoxDecorator(ComboBox comboBox) { + this.comboBox = comboBox; + } + + /** + * 设置下拉选项和当前选项的文本 + */ + public ComboBoxDecorator setToStringFunction(Function toString) { + setCellFactory(new ListCellFactory().withTextFunction(toString)); + return this; + } + + /** + * 设置下拉选项的 CellFactory + */ + public ComboBoxDecorator setCellFactory(Callback, ListCell> cellFactory) { + this.comboBox.setCellFactory(cellFactory); + this.comboBox.setButtonCell(cellFactory.call(null)); + return this; + } + + /** + * 设置当前选项的文本 + */ + public ComboBoxDecorator setButtonCellToStringFunction(Function toStringFunction) { + this.comboBox.setButtonCell(new ComboBoxListCell<>(new StringConverter() { + @Override + public String toString(T object) { + return toStringFunction.apply(object); + } + + @Override + public T fromString(String string) { + return null; + } + })); + return this; + } + + /** + * 设置选项列表 + */ + public ComboBoxDecorator setItems(Collection tCollection) { + this.comboBox.getItems().setAll(tCollection); + return this; + } + + /** + * 设置选项列表 + */ + @SafeVarargs + public final ComboBoxDecorator setItems(T... tCollection) { + this.comboBox.getItems().setAll(tCollection); + return this; + } + + /** + * 根据指定规则设置当前选项 + */ + public ComboBoxDecorator setInitialValue(Predicate valueMatcher) { + this.comboBox.getSelectionModel().select( + this.comboBox.getItems().stream().filter(valueMatcher).findFirst().orElse(null) + ); + return this; + } + + /** + * 设置当选项变化时的处理 + */ + public ComboBoxDecorator setOnChange(Consumer newValueConsumer) { + this.comboBox.getSelectionModel().selectedItemProperty().addListener( + (_ob, _old, _new) -> newValueConsumer.accept(_new) + ); + return this; + } +} diff --git a/src/main/java/com/xwintop/xcore/javafx/dialog/FxAlerts.java b/src/main/java/com/xwintop/xcore/javafx/dialog/FxAlerts.java index 96ce31ae345b67904acce5c2f88e4deefb18f893..c8eb69f5d90434a3ecb827d2eeabb271a659db13 100644 --- a/src/main/java/com/xwintop/xcore/javafx/dialog/FxAlerts.java +++ b/src/main/java/com/xwintop/xcore/javafx/dialog/FxAlerts.java @@ -1,21 +1,31 @@ package com.xwintop.xcore.javafx.dialog; import com.xwintop.xcore.javafx.FxApp; -import java.util.Optional; import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; import javafx.scene.control.TextArea; import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; import javafx.stage.Stage; +import javafx.stage.Window; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; +import java.util.Optional; + /** * 系统对话框封装 */ public class FxAlerts { + public static void info(String title, String message) { + alert(Alert.AlertType.INFORMATION, title, message); + } + + public static void warn(String title, String message) { + alert(Alert.AlertType.WARNING, title, message); + } + public static void error(String message) { alert(Alert.AlertType.ERROR, "错误", message); } @@ -24,21 +34,27 @@ public class FxAlerts { alert(Alert.AlertType.ERROR, title, message); } + public static void error(Window owner, String title, String message) { + alert(owner, Alert.AlertType.ERROR, title, message); + } + public static void error(String title, Throwable throwable) { boolean noMessage = StringUtils.isBlank(throwable.getMessage()); String message = noMessage ? throwable.toString() : throwable.getMessage(); error(title, message, ExceptionUtils.getStackTrace(throwable)); } - public static void info(String title, String message) { - alert(Alert.AlertType.INFORMATION, title, message); + public static void error(Window owner, String title, Throwable throwable) { + boolean noMessage = StringUtils.isBlank(throwable.getMessage()); + String message = noMessage ? throwable.toString() : throwable.getMessage(); + error(owner, title, message, ExceptionUtils.getStackTrace(throwable)); } - public static void warn(String title, String message) { - alert(Alert.AlertType.WARNING, title, message); + public static void alert(Alert.AlertType alertType, String title, String message) { + alert(null, alertType, title, message); } - public static void alert(Alert.AlertType alertType, String title, String message) { + public static void alert(Window owner, Alert.AlertType alertType, String title, String message) { FxApp.runLater(() -> { try { Alert alert = new Alert(alertType, message, ButtonType.OK); @@ -47,7 +63,12 @@ public class FxAlerts { Stage stage = (Stage) alert.getDialogPane().getScene().getWindow(); FxApp.setupIcon(stage); - FxApp.setupModality(alert); + + if (owner != null) { + stage.initOwner(owner); + } else { + FxApp.setupModality(alert); + } alert.showAndWait(); } catch (Exception e) { @@ -58,10 +79,15 @@ public class FxAlerts { // 打开一个展示了详细错误信息的错误对话框 public static void error(String title, String message, String details) { - FxApp.runLater(() -> error0(title, message, details)); + FxApp.runLater(() -> error0(null, title, message, details)); + } + + // 打开一个展示了详细错误信息的错误对话框 + public static void error(Window owner, String title, String message, String details) { + FxApp.runLater(() -> error0(owner, title, message, details)); } - private static void error0(String title, String message, String details) { + private static void error0(Window owner, String title, String message, String details) { Alert alert = new Alert(Alert.AlertType.ERROR); alert.setTitle(title); alert.setHeaderText(null); @@ -84,7 +110,12 @@ public class FxAlerts { Stage stage = (Stage) alert.getDialogPane().getScene().getWindow(); FxApp.setupIcon(stage); - FxApp.setupModality(alert); + + if (owner != null) { + stage.initOwner(owner); + } else { + FxApp.setupModality(alert); + } alert.showAndWait(); } diff --git a/src/main/java/com/xwintop/xcore/javafx/dialog/FxDialog.java b/src/main/java/com/xwintop/xcore/javafx/dialog/FxDialog.java index f0e2e1544e94bd727ff8933ab5e7cb731b7539e1..b6882189565ccaf5849dd3b6b9783356923dcd96 100644 --- a/src/main/java/com/xwintop/xcore/javafx/dialog/FxDialog.java +++ b/src/main/java/com/xwintop/xcore/javafx/dialog/FxDialog.java @@ -3,13 +3,6 @@ package com.xwintop.xcore.javafx.dialog; import com.xwintop.xcore.XCoreException; import com.xwintop.xcore.javafx.FxApp; import com.xwintop.xcore.util.javafx.FxmlUtil; -import java.util.HashMap; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javafx.event.ActionEvent; import javafx.event.Event; import javafx.fxml.FXMLLoader; @@ -29,6 +22,14 @@ import javafx.stage.Window; import javafx.stage.WindowEvent; import org.apache.commons.lang3.ArrayUtils; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * 自定义对话框 * @@ -48,6 +49,8 @@ public class FxDialog { private Window owner; + private ClassLoader bodyFxmlClassLoader; + private String bodyFxmlPath; private Parent body; @@ -62,11 +65,22 @@ public class FxDialog { private ResourceBundle resourceBundle; + public FxDialog setBodyFxmlClassLoader(ClassLoader bodyFxmlClassLoader) { + this.bodyFxmlClassLoader = bodyFxmlClassLoader; + return this; + } + public FxDialog setResizable(boolean resizable) { this.resizable = resizable; return this; } + public FxDialog setPrefSize(double prefWidth, double prefHeight) { + this.prefWidth = prefWidth; + this.prefHeight = prefHeight; + return this; + } + public FxDialog setPrefHeight(double prefHeight) { this.prefHeight = prefHeight; return this; @@ -92,6 +106,15 @@ public class FxDialog { return this; } + public FxDialog setBodyFxml(ClassLoader classLoader, String bodyFxmlPath) { + this.bodyFxmlClassLoader = classLoader; + this.bodyFxmlPath = bodyFxmlPath; + return this; + } + + /** + * @deprecated Use {@link #setBodyFxml(ClassLoader, String)} )} instead + */ public FxDialog setBodyFxml(String bodyFxmlPath) { this.bodyFxmlPath = bodyFxmlPath; return this; @@ -130,9 +153,12 @@ public class FxDialog { public T show() { if (this.bodyFxmlPath != null) { + ClassLoader classLoader = + this.bodyFxmlClassLoader == null ? FxDialog.class.getClassLoader() : this.bodyFxmlClassLoader; + FXMLLoader fxmlLoader = resourceBundle == null? - FxmlUtil.loadFxmlFromResource(this.bodyFxmlPath): - FxmlUtil.loadFxmlFromResource(this.bodyFxmlPath, resourceBundle); + FxmlUtil.loadFxmlFromResource(classLoader, this.bodyFxmlPath): + FxmlUtil.loadFxmlFromResource(classLoader, this.bodyFxmlPath, resourceBundle); Stage stage = createStage(fxmlLoader.getRoot()); stage.show(); @@ -151,9 +177,12 @@ public class FxDialog { public T showAndWait() { if (this.bodyFxmlPath != null) { + ClassLoader classLoader = + this.bodyFxmlClassLoader == null ? FxDialog.class.getClassLoader() : this.bodyFxmlClassLoader; + FXMLLoader fxmlLoader = resourceBundle == null? - FxmlUtil.loadFxmlFromResource(this.bodyFxmlPath): - FxmlUtil.loadFxmlFromResource(this.bodyFxmlPath, resourceBundle); + FxmlUtil.loadFxmlFromResource(classLoader, this.bodyFxmlPath): + FxmlUtil.loadFxmlFromResource(classLoader, this.bodyFxmlPath, resourceBundle); Stage stage = createStage(fxmlLoader.getRoot()); stage.showAndWait(); diff --git a/src/main/java/com/xwintop/xcore/log/package-info.java b/src/main/java/com/xwintop/xcore/log/package-info.java deleted file mode 100644 index 47acf58ff850127a5813bd49d72061cb5fa8d168..0000000000000000000000000000000000000000 --- a/src/main/java/com/xwintop/xcore/log/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - * - */ -/** - * @author Administrator - * - */ -package com.xwintop.xcore.log; \ No newline at end of file diff --git a/src/main/java/com/xwintop/xcore/plugin/PluginEvent.java b/src/main/java/com/xwintop/xcore/plugin/PluginEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..80e9463125a7f675f8d47d05c6df43ae762f7984 --- /dev/null +++ b/src/main/java/com/xwintop/xcore/plugin/PluginEvent.java @@ -0,0 +1,29 @@ +package com.xwintop.xcore.plugin; + +import javafx.event.Event; +import javafx.event.EventTarget; +import javafx.event.EventType; + +/** + * 插件生命周期相关事件,可在插件 root 节点中接收事件 + */ +public class PluginEvent extends Event { + + /** + * 插件在框架中完成初始化,即完成 FXMLLoader.load() + */ + public static final EventType PLUGIN_INITIALIZED = new EventType<>(Event.ANY, "PLUGIN_INITIALIZED"); + + /** + * 插件即将被卸载 + */ + public static final EventType PLUGIN_UNLOADING = new EventType<>(Event.ANY, "PLUGIN_UNLOADING"); + + public PluginEvent(Object source, EventTarget target, EventType eventType) { + super(source, target, eventType); + } + + public PluginEvent(Object source, EventType eventType) { + super(source, null, eventType); + } +} diff --git a/src/main/java/com/xwintop/xcore/util/KeyValue.java b/src/main/java/com/xwintop/xcore/util/KeyValue.java index 562c0b4b3b2df15a46ef734d6443c4e2229d2aae..2215d2af32ccf073ce8a66dd6a2caf1d01ae53b9 100644 --- a/src/main/java/com/xwintop/xcore/util/KeyValue.java +++ b/src/main/java/com/xwintop/xcore/util/KeyValue.java @@ -2,6 +2,9 @@ package com.xwintop.xcore.util; import java.util.Objects; +/** + * 键值对,默认是可修改的,可以通过 {@link #unmodifiable()} 方法获取不可修改的键值对。 + */ public class KeyValue { private K key; @@ -28,6 +31,31 @@ public class KeyValue { return value; } + public void setKey(K key) { + this.key = key; + } + + public void setValue(V value) { + this.value = value; + } + + /** + * 创建一个只读实例 + */ + public KeyValue unmodifiable() { + return new KeyValue<>(this) { + @Override + public void setKey(K key) { + throw new UnsupportedOperationException(); + } + + @Override + public void setValue(V value) { + throw new UnsupportedOperationException(); + } + }; + } + @Override public String toString() { return String.valueOf(key); diff --git a/src/main/java/com/xwintop/xcore/util/SystemInfoUtil.java b/src/main/java/com/xwintop/xcore/util/SystemInfoUtil.java index c51d6d3218a1e4634f597147cc5951ff55ae2c03..67278fd375cbe9a5616ef4cd026a866ceffd0a66 100644 --- a/src/main/java/com/xwintop/xcore/util/SystemInfoUtil.java +++ b/src/main/java/com/xwintop/xcore/util/SystemInfoUtil.java @@ -22,7 +22,7 @@ public class SystemInfoUtil { } else { fileName = "C://WINDOWS//system32//drivers//etc//hosts"; } - log.info("获取hosts文件路径:"+fileName); +// log.info("获取hosts文件路径:"+fileName); return fileName; } diff --git a/src/main/java/com/xwintop/xcore/util/javafx/ActionJob.java b/src/main/java/com/xwintop/xcore/util/javafx/ActionJob.java new file mode 100644 index 0000000000000000000000000000000000000000..8206892688fefb1a3565dc4104109b3f4d281de4 --- /dev/null +++ b/src/main/java/com/xwintop/xcore/util/javafx/ActionJob.java @@ -0,0 +1,13 @@ +package com.xwintop.xcore.util.javafx; + +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +public class ActionJob implements Job { + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + Runnable runnable = (Runnable) context.getMergedJobDataMap().get("runnable"); + runnable.run(); + } +} diff --git a/src/main/java/com/xwintop/xcore/util/javafx/ActionScheduleUtil.java b/src/main/java/com/xwintop/xcore/util/javafx/ActionScheduleUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..a7d25e1f3ba148a9cb9eed7cb99d777ec47bc4df --- /dev/null +++ b/src/main/java/com/xwintop/xcore/util/javafx/ActionScheduleUtil.java @@ -0,0 +1,147 @@ +package com.xwintop.xcore.util.javafx; + +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.geometry.Pos; +import javafx.scene.control.*; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.quartz.*; +import org.quartz.impl.StdSchedulerFactory; +import org.slf4j.Logger; + +/** + * @ClassName: ScheduleManager + * @Description: 自动任务管理类 + * @author: xufeng + * @date: 2018/1/25 16:35 + */ + +@Getter +@Setter +//@Slf4j +public class ActionScheduleUtil { + private static Logger log = org.slf4j.LoggerFactory.getLogger(ActionScheduleUtil.class); + private static final String[] quartzChoiceBoxStrings = new String[]{"简单表达式", "Cron表达式"}; + private SchedulerFactory sf = new StdSchedulerFactory(); + private String schedulerKeyGroup = "x"; + private String schedulerKeyName = "x"; + + private TextField cronTextField; + private ChoiceBox quartzChoiceBox; + private Spinner intervalSpinner; + private Spinner repeatCountSpinner; + + private Runnable runnable; + + //添加调度控件 + public void setScheduleNode(Pane pane) { + quartzChoiceBox = new ChoiceBox(); + quartzChoiceBox.setPrefWidth(100); + quartzChoiceBox.getItems().addAll(quartzChoiceBoxStrings); + quartzChoiceBox.getSelectionModel().select(0); + StackPane stackPane = new StackPane(); + cronTextField = new TextField("* * * * * ?"); + cronTextField.setVisible(false); + stackPane.getChildren().add(cronTextField); + HBox simpleScheduleAnchorPane = new HBox(); + simpleScheduleAnchorPane.setAlignment(Pos.CENTER_LEFT); + simpleScheduleAnchorPane.setSpacing(5); + stackPane.getChildren().add(simpleScheduleAnchorPane); + intervalSpinner = new Spinner<>(); + intervalSpinner.setEditable(true); + intervalSpinner.setPrefWidth(66); + intervalSpinner.setTooltip(new Tooltip("单位为秒")); + repeatCountSpinner = new Spinner<>(); + repeatCountSpinner.setEditable(true); + repeatCountSpinner.setPrefWidth(66); + repeatCountSpinner.setTooltip(new Tooltip("-1为永久任务")); + simpleScheduleAnchorPane.getChildren().add(new Label("间隔")); + simpleScheduleAnchorPane.getChildren().add(intervalSpinner); + simpleScheduleAnchorPane.getChildren().add(new Label("次数")); + simpleScheduleAnchorPane.getChildren().add(repeatCountSpinner); + JavaFxViewUtil.setSpinnerValueFactory(intervalSpinner, 1, Integer.MAX_VALUE); + JavaFxViewUtil.setSpinnerValueFactory(repeatCountSpinner, -1, Integer.MAX_VALUE); + quartzChoiceBox.valueProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, String oldValue, String newValue) { + if (quartzChoiceBoxStrings[0].equals(newValue)) { + cronTextField.setVisible(false); + simpleScheduleAnchorPane.setVisible(true); + } else if (quartzChoiceBoxStrings[1].equals(newValue)) { + cronTextField.setVisible(true); + simpleScheduleAnchorPane.setVisible(false); + } + } + }); + Button runQuartzButton = new Button("定时运行"); + runQuartzButton.setOnAction(event -> { + if ("定时运行".equals(runQuartzButton.getText())) { + try { + ActionScheduleUtil.this.runQuartzAction(); + runQuartzButton.setText("停止运行"); + } catch (Exception e) { + log.error("运行错误!", e); + TooltipUtil.showToast("运行错误:" + e.getMessage()); + } + } else { + try { + ActionScheduleUtil.this.stopQuartzAction(); + runQuartzButton.setText("定时运行"); + } catch (Exception e) { + log.error("停止错误!", e); + TooltipUtil.showToast("停止错误:" + e.getMessage()); + } + } + }); + pane.getChildren().add(quartzChoiceBox); + pane.getChildren().add(stackPane); + pane.getChildren().add(runQuartzButton); + } + + //设置调度执行事件 + public void setJobAction(Runnable runnable) { + this.runnable = runnable; + } + + public void runQuartzAction() throws Exception { +// schedulerKeyGroup = jobClass.toString(); + schedulerKeyName = schedulerKeyGroup + System.currentTimeMillis(); + JobDetail jobDetail = JobBuilder.newJob(ActionJob.class).withIdentity(schedulerKeyName, schedulerKeyGroup).build(); + jobDetail.getJobDataMap().put("runnable", runnable); + ScheduleBuilder scheduleBuilder = null; + if (quartzChoiceBoxStrings[0].equals(quartzChoiceBox.getValue())) { + scheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(intervalSpinner.getValue())// 时间间隔 + .withRepeatCount(repeatCountSpinner.getValue());// 重复次数 + } else if (quartzChoiceBoxStrings[1].equals(quartzChoiceBox.getValue())) { + if (StringUtils.isEmpty(cronTextField.getText())) { + throw new Exception("cron表达式不能为空。"); + } + scheduleBuilder = CronScheduleBuilder.cronSchedule(cronTextField.getText()); + } + // 描叙触发Job执行的时间触发规则,Trigger实例化一个触发器 + Trigger trigger = TriggerBuilder.newTrigger()// 创建一个新的TriggerBuilder来规范一个触发器 + .withIdentity(schedulerKeyName, schedulerKeyGroup)// 给触发器一个名字和组名 + .startNow()// 立即执行 + .withSchedule(scheduleBuilder).build();// 产生触发器 + + // 运行容器,使用SchedulerFactory创建Scheduler实例 + Scheduler scheduler = sf.getScheduler(); + // 向Scheduler添加一个job和trigger + scheduler.scheduleJob(jobDetail, trigger); + if (!scheduler.isStarted()) { + scheduler.start(); + } + } + + public void stopQuartzAction() throws Exception { + Scheduler sched = sf.getScheduler(); + sched.unscheduleJob(new TriggerKey(schedulerKeyName, schedulerKeyGroup)); + sched.deleteJob(new JobKey(schedulerKeyName, schedulerKeyGroup)); + } +} diff --git a/src/main/java/com/xwintop/xcore/util/javafx/AlertUtil.java b/src/main/java/com/xwintop/xcore/util/javafx/AlertUtil.java index 59168335df8518dcf362ead31d7cc6faf451e0be..ab33c40c75633e0687f7215fede341c24b2ecb0d 100644 --- a/src/main/java/com/xwintop/xcore/util/javafx/AlertUtil.java +++ b/src/main/java/com/xwintop/xcore/util/javafx/AlertUtil.java @@ -6,13 +6,14 @@ import static com.xwintop.xcore.javafx.helper.LayoutHelper.vbox; import com.xwintop.xcore.javafx.FxApp; import com.xwintop.xcore.javafx.dialog.FxDialog; import com.xwintop.xcore.javafx.helper.LayoutHelper; + +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import javafx.geometry.Insets; import javafx.geometry.Pos; -import javafx.scene.control.Button; -import javafx.scene.control.ButtonType; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; +import javafx.scene.control.*; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Modality; @@ -164,4 +165,51 @@ public class AlertUtil { newStage.showAndWait(); return isOk.get(); } + + @Deprecated + public static String showInputAlert(String message) { + return showInputAlertDefaultValue(message, null); + } + + public static String[] showInputAlert(String message, String... names) { + return showInputAlertMore(message, names); + } + + public static String[] showInputAlertMore(String message, String... names) { + return showInputAlertMore(message, names, new String[names.length]); + } + + public static String[] showInputAlertMore(String message, String[] names, String[] defaultValue) { + GridPane page1Grid = new GridPane(); + page1Grid.setVgap(10); + page1Grid.setHgap(10); + + TextField[] textFields = new TextField[names.length]; + for (int i = 0; i < names.length; i++) { + TextField textField = new TextField(); + textField.setText(defaultValue[i]); + textField.setMinWidth(100); + textField.prefColumnCountProperty().bind(textField.textProperty().length()); + GridPane.setHgrow(textField, Priority.ALWAYS); + page1Grid.add(new Label(names[i]), 0, i); + page1Grid.add(textField, 1, i); + textFields[i] = textField; + } + + Alert alert = new Alert(Alert.AlertType.NONE, null, new ButtonType("取消", ButtonBar.ButtonData.NO), + new ButtonType("确定", ButtonBar.ButtonData.YES)); + alert.setTitle(message); + alert.setGraphic(page1Grid); + alert.setWidth(200); + Optional _buttonType = alert.showAndWait(); + // 根据点击结果返回 + if (_buttonType.get().getButtonData().equals(ButtonBar.ButtonData.YES)) { + String[] stringS = new String[names.length]; + for (int i = 0; i < textFields.length; i++) { + stringS[i] = textFields[i].getText(); + } + return stringS; + } + return null; + } } diff --git a/src/main/java/com/xwintop/xcore/util/javafx/FileChooserUtil.java b/src/main/java/com/xwintop/xcore/util/javafx/FileChooserUtil.java index 8794db991794be5c19e9aff3788a7ac514d9f5c6..c6c36cac9a741ec75bb25b954362baed230c9ea2 100644 --- a/src/main/java/com/xwintop/xcore/util/javafx/FileChooserUtil.java +++ b/src/main/java/com/xwintop/xcore/util/javafx/FileChooserUtil.java @@ -10,6 +10,7 @@ import javafx.stage.FileChooser.ExtensionFilter; import javax.swing.filechooser.FileSystemView; import java.io.File; +import java.util.List; /** * 文件选择工具 @@ -20,6 +21,22 @@ public class FileChooserUtil { public static final File HOME_DIRECTORY = FileSystemView.getFileSystemView().getHomeDirectory(); + //选择多个文件 + public static List chooseFiles() { + return chooseFiles(null); + } + + public static List chooseFiles(ExtensionFilter... extensionFilter) { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("请选择文件"); + fileChooser.setInitialDirectory(HOME_DIRECTORY); + + if (extensionFilter != null) { + fileChooser.getExtensionFilters().addAll(extensionFilter); + } + return fileChooser.showOpenMultipleDialog(null); + } + public static File chooseFile() { return chooseFile(null); } diff --git a/src/main/java/com/xwintop/xcore/util/javafx/FxmlUtil.java b/src/main/java/com/xwintop/xcore/util/javafx/FxmlUtil.java index b3d77cd79467c3e0fc01a8f4fe06c98ac4e8b2ee..c04aaeccce4cbf36639e5a8fdc0b770eefa05c14 100644 --- a/src/main/java/com/xwintop/xcore/util/javafx/FxmlUtil.java +++ b/src/main/java/com/xwintop/xcore/util/javafx/FxmlUtil.java @@ -1,20 +1,41 @@ package com.xwintop.xcore.util.javafx; import com.xwintop.xcore.XCoreException; +import javafx.fxml.FXMLLoader; +import org.apache.commons.lang3.StringUtils; + import java.io.IOException; import java.util.ResourceBundle; -import javafx.fxml.FXMLLoader; +/** + * 调用者最好自己提供 ClassLoader 用于加载自身的资源文件, + * 否则使用 xcore 的 ClassLoader 加载可能会由于安全原因而失败 + */ public class FxmlUtil { + /** + * @deprecated Use {@link #loadFxmlFromResource(ClassLoader, String)} )} instead. + */ public static FXMLLoader loadFxmlFromResource(String resourcePath) { - return loadFxmlFromResource(resourcePath, null); + return loadFxmlFromResource(FxmlUtil.class.getClassLoader(), resourcePath, null); } + /** + * @deprecated Use {@link #loadFxmlFromResource(ClassLoader, String, ResourceBundle)} instead. + */ public static FXMLLoader loadFxmlFromResource(String resourcePath, ResourceBundle resourceBundle) { + return loadFxmlFromResource(FxmlUtil.class.getClassLoader(), resourcePath, resourceBundle); + } + + public static FXMLLoader loadFxmlFromResource(ClassLoader classLoader, String resourcePath) { + return loadFxmlFromResource(classLoader, resourcePath, null); + } + + public static FXMLLoader loadFxmlFromResource( + ClassLoader classLoader, String resourcePath, ResourceBundle resourceBundle) { try { FXMLLoader fxmlLoader = new FXMLLoader(); - fxmlLoader.setLocation(FxmlUtil.class.getResource(resourcePath)); + fxmlLoader.setLocation(classLoader.getResource(StringUtils.removeStart(resourcePath, "/"))); fxmlLoader.setResources(resourceBundle); fxmlLoader.load(); return fxmlLoader; diff --git a/src/main/java/com/xwintop/xcore/util/javafx/ImageUtil.java b/src/main/java/com/xwintop/xcore/util/javafx/ImageUtil.java index 1a46f9290b237ca4eff801ac9819e877e8609ecd..44340b4caa1e3c926f259ed72389997d3c0a4ce2 100644 --- a/src/main/java/com/xwintop/xcore/util/javafx/ImageUtil.java +++ b/src/main/java/com/xwintop/xcore/util/javafx/ImageUtil.java @@ -2,6 +2,7 @@ package com.xwintop.xcore.util.javafx; import com.xwintop.xcore.util.FileUtil; import javafx.embed.swing.SwingFXUtils; +import javafx.scene.image.*; import javafx.scene.image.Image; import lombok.Getter; import lombok.Setter; @@ -9,6 +10,9 @@ import org.apache.commons.imaging.ImageFormats; import org.apache.commons.imaging.Imaging; import javax.imageio.ImageIO; +import javax.swing.*; +import javax.swing.filechooser.FileSystemView; +import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.File; @@ -26,6 +30,7 @@ public class ImageUtil { /** * 获取图片BufferedImage + * * @param path 图片路径 */ public static BufferedImage getBufferedImage(String path) { @@ -48,6 +53,7 @@ public class ImageUtil { /** * 获取javafx图片 + * * @param path 图片路径 */ public static Image getFXImage(String path) { @@ -77,19 +83,107 @@ public class ImageUtil { /** * 保存图片 + * * @param image * @param file */ - public static void writeImage(Image image, File file) throws Exception{ - writeImage(SwingFXUtils.fromFXImage(image, null),file); + public static void writeImage(Image image, File file) throws Exception { + writeImage(SwingFXUtils.fromFXImage(image, null), file); } - public static void writeImage(BufferedImage bufferedImage, File file) throws Exception{ + public static void writeImage(BufferedImage bufferedImage, File file) throws Exception { try { - Imaging.writeImage(bufferedImage,file, ImageFormats.valueOf(FileUtil.getFileSuffixName(file).toUpperCase()),null); + Imaging.writeImage(bufferedImage, file, ImageFormats.valueOf(FileUtil.getFileSuffixName(file).toUpperCase()), null); } catch (Exception e) { e.printStackTrace(); - ImageIO.write(bufferedImage, FileUtil.getFileSuffixName(file),file); + ImageIO.write(bufferedImage, FileUtil.getFileSuffixName(file), file); } } + + //获取文件夹图标 + public static ImageView getDirectoryIconImage() { + return getFileIconImage(FileSystemView.getFileSystemView().getHomeDirectory()); + } + + //获取文件图标 + public static ImageView getFileIconImage(File file) { + Icon icon = FileSystemView.getFileSystemView().getSystemIcon(file); + BufferedImage bufferedImage = new BufferedImage( + icon.getIconWidth(), + icon.getIconHeight(), + BufferedImage.TYPE_INT_ARGB + ); + icon.paintIcon(null, bufferedImage.getGraphics(), 0, 0); + Image fxImage = SwingFXUtils.toFXImage(bufferedImage, null); + return new ImageView(fxImage); + } + + /** + * 根据传入类型变化图片像素 + */ + public static javafx.scene.image.Image pixWithImage(int type, javafx.scene.image.Image image) { + PixelReader pixelReader = image.getPixelReader(); + if (image.getWidth() > 0 && image.getHeight() > 0) { + WritableImage wImage; + wImage = new WritableImage((int) image.getWidth(), (int) image.getHeight()); + PixelWriter pixelWriter = wImage.getPixelWriter(); + + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + javafx.scene.paint.Color color = pixelReader.getColor(x, y); + switch (type) { + case 1: + // 颜色变轻 + color = color.brighter(); + break; + case 2: + // 颜色变深 + color = color.darker(); + break; + case 3: + // 灰度化 + color = color.grayscale(); + break; + case 4: + // 颜色反转 + color = color.invert(); + break; + case 5: + // 颜色饱和 + color = color.saturate(); + break; + case 6: + // 颜色不饱和 + color = color.desaturate(); + break; + case 7: + // 颜色灰度化后反转(字黑体,背景鲜亮,可用于强字弱景) + color = color.grayscale(); + color = color.invert(); + break; + case 8: + // 颜色透明 + if (color.getOpacity() == 0) { + color = new javafx.scene.paint.Color(color.getRed(), color.getGreen(), color.getBlue(), 0); + } else { + color = new javafx.scene.paint.Color(color.getRed(), color.getGreen(), color.getBlue(), 0.5); + } + break; + default: + break; + } + + pixelWriter.setColor(x, y, color); + } + } + return wImage; + } + return null; + } + + //将javafx中Color转换为awt中Color + public static java.awt.Color getAwtColor(javafx.scene.paint.Color color) { + java.awt.Color colorw = new Color((float) color.getRed(), (float) color.getGreen(), (float) color.getBlue(), (float) color.getOpacity()); + return colorw; + } } diff --git a/src/main/java/com/xwintop/xcore/util/javafx/JavaFxDragResizer.java b/src/main/java/com/xwintop/xcore/util/javafx/JavaFxDragResizer.java new file mode 100644 index 0000000000000000000000000000000000000000..c742ac8260be4ca9eec7a736c6b60f0669cc2a13 --- /dev/null +++ b/src/main/java/com/xwintop/xcore/util/javafx/JavaFxDragResizer.java @@ -0,0 +1,127 @@ +package com.xwintop.xcore.util.javafx; + +import javafx.scene.Cursor; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Region; + +/** + * @ClassName: JavaFxDragResizer + * @Description: 设置控件自定义大小 + * @author: xufeng + * @date: 2021/12/25 14:40 + */ + +public class JavaFxDragResizer { + + /** + * The margin around the control that a user can click in to start resizing + * the region. + */ + private static final int RESIZE_MARGIN = 10; + private final Region region; + private double y; + private double x; + private boolean initMinHeight; + private boolean initMinWidth; + private boolean draggableZoneX, draggableZoneY; + private boolean dragging; + private boolean draggableX = true; + private boolean draggableY = true; + + private JavaFxDragResizer(Region aRegion) { + region = aRegion; + this.setOnMouse(); + } + + private JavaFxDragResizer(Region aRegion, boolean draggableX, boolean draggableY) { + region = aRegion; + this.draggableX = draggableX; + this.draggableY = draggableY; + this.setOnMouse(); + } + + public static void makeResizableX(Region region) { + new JavaFxDragResizer(region, true, false); + } + + public static void makeResizableY(Region region) { + new JavaFxDragResizer(region, false, true); + } + + public static void makeResizable(Region region) { + new JavaFxDragResizer(region); + } + + protected void setOnMouse() { + region.setOnMousePressed(event -> this.mousePressed(event)); + region.setOnMouseDragged(event -> this.mouseDragged(event)); + region.setOnMouseMoved(event -> this.mouseOver(event)); + region.setOnMouseReleased(event -> this.mouseReleased(event)); + } + + protected void mouseReleased(MouseEvent event) { + dragging = false; + region.setCursor(Cursor.DEFAULT); + } + + protected void mouseOver(MouseEvent event) { + if (isInDraggableZone(event) || dragging) { + if (draggableZoneY && draggableZoneX) { + region.setCursor(Cursor.SE_RESIZE); + } else if (draggableZoneY) { + region.setCursor(Cursor.S_RESIZE); + } else if (draggableZoneX) { + region.setCursor(Cursor.E_RESIZE); + } + } else { + region.setCursor(Cursor.DEFAULT); + } + } + + + //had to use 2 variables for the controll, tried without, had unexpected behaviour (going big was ok, going small nope.) + protected boolean isInDraggableZone(MouseEvent event) { + draggableZoneY = draggableY && (boolean) (event.getY() > (region.getHeight() - RESIZE_MARGIN)); + draggableZoneX = draggableX && (boolean) (event.getX() > (region.getWidth() - RESIZE_MARGIN)); + return (draggableZoneY || draggableZoneX); + } + + protected void mouseDragged(MouseEvent event) { + if (!dragging) { + return; + } + if (draggableY && draggableZoneY) { + double mousey = event.getY(); + double newHeight = region.getMinHeight() + (mousey - y); + region.setMinHeight(newHeight); + y = mousey; + } + if (draggableX && draggableZoneX) { + double mousex = event.getX(); + double newWidth = region.getMinWidth() + (mousex - x); + region.setMinWidth(newWidth); + x = mousex; + } + } + + protected void mousePressed(MouseEvent event) { + // ignore clicks outside of the draggable margin + if (!isInDraggableZone(event)) { + return; + } + dragging = true; + // make sure that the minimum height is set to the current height once, + // setting a min height that is smaller than the current height will + // have no effect + if (!initMinHeight) { + region.setMinHeight(region.getHeight()); + initMinHeight = true; + } + y = event.getY(); + if (!initMinWidth) { + region.setMinWidth(region.getWidth()); + initMinWidth = true; + } + x = event.getX(); + } +} \ No newline at end of file diff --git a/src/main/java/com/xwintop/xcore/util/javafx/JavaFxSystemUtil.java b/src/main/java/com/xwintop/xcore/util/javafx/JavaFxSystemUtil.java index c7d9cc6dd5872f421b1a02c35d76027fc91c46fc..5248d84c6ddbefb85720b88f7eb49616b55e1ccd 100644 --- a/src/main/java/com/xwintop/xcore/util/javafx/JavaFxSystemUtil.java +++ b/src/main/java/com/xwintop/xcore/util/javafx/JavaFxSystemUtil.java @@ -5,14 +5,16 @@ import java.awt.Dimension; import java.awt.Toolkit; import java.io.File; import java.io.IOException; + import javafx.geometry.Rectangle2D; import javafx.stage.Screen; import javafx.stage.Stage; import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; -@Slf4j +//@Slf4j public class JavaFxSystemUtil { - + private static Logger log = org.slf4j.LoggerFactory.getLogger(JavaFxSystemUtil.class); /** * @deprecated 使用 {@link com.xwintop.xcore.javafx.FxApp#primaryStage} */ @@ -21,6 +23,7 @@ public class JavaFxSystemUtil { /** * 打开目录 + * * @param directoryPath 目录路径 */ public static void openDirectory(String directoryPath) { @@ -31,14 +34,17 @@ public class JavaFxSystemUtil { } } - /** * 获取屏幕尺寸 + * * @param width 宽度比 * @param height 高度比 * @return 屏幕尺寸 */ public static double[] getScreenSizeByScale(double width, double height) { +// if (WebAPI.isBrowser()) { +// return new double[]{1280, 900}; +// } Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); double screenWidth = screenSize.width * width; double screenHeight = screenSize.height * height; diff --git a/src/main/java/com/xwintop/xcore/util/javafx/JavaFxViewUtil.java b/src/main/java/com/xwintop/xcore/util/javafx/JavaFxViewUtil.java index a535731809630d35b2aa71237fa05ca5128c1af5..e8eb294c56fddb89473779a16a811ae42188c233 100644 --- a/src/main/java/com/xwintop/xcore/util/javafx/JavaFxViewUtil.java +++ b/src/main/java/com/xwintop/xcore/util/javafx/JavaFxViewUtil.java @@ -4,6 +4,8 @@ import cn.hutool.cache.impl.TimedCache; import cn.hutool.core.lang.Singleton; import com.jfoenix.controls.JFXDecorator; import com.xwintop.xcore.javafx.FxApp; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ObservableValue; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.event.Event; @@ -21,7 +23,6 @@ import javafx.scene.control.cell.TextFieldListCell; import javafx.scene.control.cell.TextFieldTableCell; import javafx.scene.image.Image; import javafx.scene.image.ImageView; -import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.StackPane; @@ -35,6 +36,7 @@ import javafx.util.StringConverter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.MethodUtils; +import org.slf4j.Logger; import java.lang.reflect.Method; import java.net.URL; @@ -45,9 +47,9 @@ import java.util.function.Consumer; import static com.xwintop.xcore.javafx.helper.LayoutHelper.iconView; -@Slf4j +//@Slf4j public class JavaFxViewUtil { - + private static Logger log = org.slf4j.LoggerFactory.getLogger(JavaFxViewUtil.class); /** * 创建对话框窗体 * @@ -58,6 +60,7 @@ public class JavaFxViewUtil { * @param fullScreenButton 是否显示全屏按钮 * @param maximizeButton 是否显示最大化按钮 * @param minimizeButton 是否显示最小化按钮 + * * @return 新建的窗体对象 */ public static Stage jfxStage( @@ -210,8 +213,13 @@ public class JavaFxViewUtil { public static void openNewWindow(String title, String iconUrl, Parent root, double width, double height, boolean fullScreen, boolean max, boolean min) { Stage newStage = getNewStageNull(title, iconUrl, root, width, height, fullScreen, max, min); - newStage.initModality(Modality.APPLICATION_MODAL); - newStage.show(); +// if (WebAPI.isBrowser()) { +// WebAPI webAPI = WebAPI.getWebAPI(JavaFxSystemUtil.mainStage); +// webAPI.openStageAsTab(newStage); +// } else { + newStage.initModality(Modality.APPLICATION_MODAL); + newStage.show(); +// } } //获取一个新窗口 @@ -327,29 +335,25 @@ public class JavaFxViewUtil { */ public static void addTableViewOnMouseRightClickMenu(TableView> tableView) { tableView.setEditable(true); - tableView.setOnMouseClicked(event -> { - if (event.getButton() == MouseButton.SECONDARY) { - MenuItem menuAdd = new MenuItem("添加行"); - menuAdd.setOnAction(event1 -> { - tableView.getItems().add(new HashMap()); - }); - MenuItem menu_Copy = new MenuItem("复制选中行"); - menu_Copy.setOnAction(event1 -> { - Map map = tableView.getSelectionModel().getSelectedItem(); - Map map2 = new HashMap(map); - tableView.getItems().add(tableView.getSelectionModel().getSelectedIndex(), map2); - }); - MenuItem menu_Remove = new MenuItem("删除选中行"); - menu_Remove.setOnAction(event1 -> { - tableView.getItems().remove(tableView.getSelectionModel().getSelectedIndex()); - }); - MenuItem menu_RemoveAll = new MenuItem("删除所有"); - menu_RemoveAll.setOnAction(event1 -> { - tableView.getItems().clear(); - }); - tableView.setContextMenu(new ContextMenu(menuAdd, menu_Copy, menu_Remove, menu_RemoveAll)); - } + MenuItem menuAdd = new MenuItem("添加行"); + menuAdd.setOnAction(event1 -> { + tableView.getItems().add(new HashMap()); + }); + MenuItem menu_Copy = new MenuItem("复制选中行"); + menu_Copy.setOnAction(event1 -> { + Map map = tableView.getSelectionModel().getSelectedItem(); + Map map2 = new HashMap(map); + tableView.getItems().add(tableView.getSelectionModel().getSelectedIndex(), map2); + }); + MenuItem menu_Remove = new MenuItem("删除选中行"); + menu_Remove.setOnAction(event1 -> { + tableView.getItems().remove(tableView.getSelectionModel().getSelectedIndex()); }); + MenuItem menu_RemoveAll = new MenuItem("删除所有"); + menu_RemoveAll.setOnAction(event1 -> { + tableView.getItems().clear(); + }); + tableView.setContextMenu(new ContextMenu(menuAdd, menu_Copy, menu_Remove, menu_RemoveAll)); } /** @@ -359,27 +363,46 @@ public class JavaFxViewUtil { public static void addListViewOnMouseRightClickMenu(ListView listView) { listView.setEditable(true); listView.setCellFactory(TextFieldListCell.forListView()); - listView.setOnMouseClicked(event -> { - if (event.getButton() == MouseButton.SECONDARY) { - MenuItem menuAdd = new MenuItem("添加行"); - menuAdd.setOnAction(event1 -> { - listView.getItems().add(""); - }); - MenuItem menu_Copy = new MenuItem("复制选中行"); - menu_Copy.setOnAction(event1 -> { - listView.getItems().add(listView.getSelectionModel().getSelectedIndex(), - listView.getSelectionModel().getSelectedItem()); - }); - MenuItem menu_Remove = new MenuItem("删除选中行"); - menu_Remove.setOnAction(event1 -> { - listView.getItems().remove(listView.getSelectionModel().getSelectedIndex()); - }); - MenuItem menu_RemoveAll = new MenuItem("删除所有"); - menu_RemoveAll.setOnAction(event1 -> { - listView.getItems().clear(); - }); - listView.setContextMenu(new ContextMenu(menuAdd, menu_Copy, menu_Remove, menu_RemoveAll)); - } + MenuItem menuAdd = new MenuItem("添加行"); + menuAdd.setOnAction(event1 -> { + listView.getItems().add(""); + }); + MenuItem menu_Copy = new MenuItem("复制选中行"); + menu_Copy.setOnAction(event1 -> { + listView.getItems().add(listView.getSelectionModel().getSelectedIndex(), + listView.getSelectionModel().getSelectedItem()); + }); + MenuItem menu_Remove = new MenuItem("删除选中行"); + menu_Remove.setOnAction(event1 -> { + listView.getItems().remove(listView.getSelectionModel().getSelectedIndex()); + }); + MenuItem menu_RemoveAll = new MenuItem("删除所有"); + menu_RemoveAll.setOnAction(event1 -> { + listView.getItems().clear(); + }); + listView.setContextMenu(new ContextMenu(menuAdd, menu_Copy, menu_Remove, menu_RemoveAll)); + } + + /** + * @Title: setTableColumnIndex + * @Description: 设置表格属性为序号 + */ + public static void setTableColumnIndex(TableColumn tableColumn) { + tableColumn.setCellValueFactory((Callback) param -> new SimpleObjectProperty<>(String.valueOf(param.getTableView().getItems().indexOf(param.getValue()) + 1))); + tableColumn.setCellFactory((col) -> { + TableCell cell = new TableCell() { + @Override + public void updateItem(Object item, boolean empty) { + super.updateItem(item, empty); + this.setText(null); + this.setGraphic(null); + if (!empty) { + int rowIndex = this.getIndex() + 1; + this.setText(String.valueOf(rowIndex)); + } + } + }; + return cell; }); } diff --git a/src/main/java/com/xwintop/xcore/util/javafx/TextFieldInputHistoryDialog.java b/src/main/java/com/xwintop/xcore/util/javafx/TextFieldInputHistoryDialog.java index e9b4e5101474ec9841cf9ff601ea1733d4c16b7a..4ea826593496d347850a9748a4d93dbbd848b240 100644 --- a/src/main/java/com/xwintop/xcore/util/javafx/TextFieldInputHistoryDialog.java +++ b/src/main/java/com/xwintop/xcore/util/javafx/TextFieldInputHistoryDialog.java @@ -9,6 +9,7 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; import org.yaml.snakeyaml.Yaml; import java.io.File; @@ -28,8 +29,9 @@ import java.util.function.Function; @Getter @Setter -@Slf4j +//@Slf4j public class TextFieldInputHistoryDialog { + private static Logger log = org.slf4j.LoggerFactory.getLogger(TextFieldInputHistoryDialog.class); private ObservableList> tableData = FXCollections.observableArrayList(); private String saveFilePath = null; private String[] tableColumns = null; @@ -86,7 +88,7 @@ public class TextFieldInputHistoryDialog { try { File CONFIG_FILE = new File(saveFilePath); Yaml yaml = new Yaml(); - FileUtils.writeStringToFile(CONFIG_FILE, yaml.dump(this.getTableData()), "UTF-8"); + FileUtils.writeStringToFile(CONFIG_FILE, yaml.dump(this.tableData), "UTF-8"); TooltipUtil.showToast("保存配置成功,保存在:" + CONFIG_FILE.getPath()); } catch (Exception e) { log.error("保存配置失败", e); diff --git a/src/main/java/module-info.java.bak b/src/main/java/module-info.java.bak new file mode 100644 index 0000000000000000000000000000000000000000..3c9c665b9a35ca36c092f801ee3fe350c5abe7e0 --- /dev/null +++ b/src/main/java/module-info.java.bak @@ -0,0 +1,26 @@ +open module com.xwintop.xcore { + exports com.xwintop.xcore; + requires java.base; + requires java.desktop; + requires javafx.base; + requires javafx.controls; + requires javafx.fxml; + requires javafx.graphics; + requires javafx.web; + requires org.apache.commons.lang3; + requires org.apache.commons.io; + requires hutool.all; + requires com.jfoenix; + requires lombok; + requires org.slf4j; + requires logback.classic; + requires logback.core; + requires okhttp3; + requires dom4j; + requires org.yaml.snakeyaml; + requires quartz; + requires org.apache.commons.collections4; + requires javafx.swing; + requires org.apache.commons.imaging; + requires org.controlsfx.controls; +} diff --git a/src/test/java/com/xwintop/xcore/javafx/dialog/FxDialogTest.java b/src/test/java/com/xwintop/xcore/javafx/dialog/FxDialogTest.java index 85adf9866bc21bd870938813524a95183cb94820..889e2ef0f0b2f1cfade833f9e7edb0c2ed1729f8 100644 --- a/src/test/java/com/xwintop/xcore/javafx/dialog/FxDialogTest.java +++ b/src/test/java/com/xwintop/xcore/javafx/dialog/FxDialogTest.java @@ -1,7 +1,5 @@ package com.xwintop.xcore.javafx.dialog; -import static com.xwintop.xcore.javafx.helper.LayoutHelper.button; - import com.xwintop.xcore.javafx.FxApp; import com.xwintop.xcore.javafx.helper.LayoutHelper; import javafx.application.Application; @@ -10,75 +8,81 @@ import javafx.scene.control.ButtonType; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; -public class FxDialogTest extends Application { - - public static void main(String[] args) { - launch(args); - } - - @Override - public void start(Stage primaryStage) throws Exception { - FxApp.init(primaryStage, "/icon.png"); - - primaryStage.setScene(new Scene(new BorderPane( - LayoutHelper.vbox(10, 10, - button("打开带按钮的对话框", () -> openDialog(primaryStage)), - button("打开不带按钮的对话框", () -> openDialog2(primaryStage)), - button("不可通过顶部按钮关闭的对话框", () -> openDialog3(primaryStage)) - ) - ), 400, 300)); - primaryStage.show(); - } - - private void openDialog(Stage primaryStage) { - - // 创建对话框 - FxDialog dialog = new FxDialog() - .setOwner(primaryStage) - .setBodyFxml("/sample-dialog-body.fxml") - .setTitle("对话框标题") - .setButtonTypes(ButtonType.OK, ButtonType.CANCEL); - - // 通过 Controller 对象初始化对话框内容 - FxDialogTestController controller = dialog.show(); - controller.initName("这是一个名字"); - - // 用户如果修改了对话框内容,仍然通过 Controller 来获取 - // 这样避免了父窗体代码直接访问子窗体元素,职责混乱 - dialog.setButtonHandler(ButtonType.OK, (actionEvent, stage) -> { - System.out.println("新的名字:" + controller.getName()); - stage.close(); - }); - - dialog.setButtonHandler(ButtonType.CANCEL, (actionEvent, stage) -> { - - }); - } - - private void openDialog2(Stage primaryStage) { +import static com.xwintop.xcore.javafx.helper.LayoutHelper.button; +import static javafx.application.Application.launch; - // 创建不带按钮的对话框 - FxDialog dialog = new FxDialog() - .setOwner(primaryStage) - .setBodyFxml("/sample-dialog-body.fxml") - .setTitle("对话框标题"); +public class FxDialogTest { - // 通过 Controller 对象初始化对话框内容 - FxDialogTestController controller = dialog.show(); - controller.initName("这是一个名字"); + public static void main(String[] args) { + launch(FxDialogTextApp.class, args); } - private void openDialog3(Stage primaryStage) { - - // 创建不带按钮的对话框 - FxDialog dialog = new FxDialog() - .setOwner(primaryStage) - .setCloseable(false) - .setBodyFxml("/sample-dialog-body.fxml") - .setButtonTypes(ButtonType.CLOSE) - .setButtonHandler(ButtonType.CLOSE, (event, stage) -> stage.close()) - .setTitle("对话框"); - - dialog.show(); + public static class FxDialogTextApp extends Application { + + @Override + public void start(Stage primaryStage) throws Exception { + FxApp.init(primaryStage, "/icon.png"); + + primaryStage.setScene(new Scene(new BorderPane( + LayoutHelper.vbox(10, 10, + button("打开带按钮的对话框", () -> openDialog(primaryStage)), + button("打开不带按钮的对话框", () -> openDialog2(primaryStage)), + button("不可通过顶部按钮关闭的对话框", () -> openDialog3(primaryStage)) + ) + ), 400, 300)); + primaryStage.show(); + } + + private void openDialog(Stage primaryStage) { + + // 创建对话框 + FxDialog dialog = new FxDialog() + .setOwner(primaryStage) + .setBodyFxml(getClass().getClassLoader(), "/sample-dialog-body.fxml") + .setTitle("对话框标题") + .setButtonTypes(ButtonType.OK, ButtonType.CANCEL); + + // 通过 Controller 对象初始化对话框内容 + FxDialogTestController controller = dialog.show(); + controller.initName("这是一个名字"); + + // 用户如果修改了对话框内容,仍然通过 Controller 来获取 + // 这样避免了父窗体代码直接访问子窗体元素,职责混乱 + dialog.setButtonHandler(ButtonType.OK, (actionEvent, stage) -> { + System.out.println("新的名字:" + controller.getName()); + stage.close(); + }); + + dialog.setButtonHandler(ButtonType.CANCEL, (actionEvent, stage) -> { + + }); + } + + private void openDialog2(Stage primaryStage) { + + // 创建不带按钮的对话框 + FxDialog dialog = new FxDialog() + .setOwner(primaryStage) + .setBodyFxml(getClass().getClassLoader(), "/sample-dialog-body.fxml") + .setTitle("对话框标题"); + + // 通过 Controller 对象初始化对话框内容 + FxDialogTestController controller = dialog.show(); + controller.initName("这是一个名字"); + } + + private void openDialog3(Stage primaryStage) { + + // 创建不带按钮的对话框 + FxDialog dialog = new FxDialog() + .setOwner(primaryStage) + .setCloseable(false) + .setBodyFxml(getClass().getClassLoader(), "/sample-dialog-body.fxml") + .setButtonTypes(ButtonType.CLOSE) + .setButtonHandler(ButtonType.CLOSE, (event, stage) -> stage.close()) + .setTitle("对话框"); + + dialog.show(); + } } -} \ No newline at end of file +}