diff --git a/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java b/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java index 7f38459..0d47795 100644 --- a/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java +++ b/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java @@ -2,6 +2,7 @@ package org.jcnc.jnotepad.app.manager; import atlantafx.base.theme.PrimerLight; import javafx.application.Application; +import javafx.application.Platform; import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.stage.Stage; @@ -14,10 +15,12 @@ import org.jcnc.jnotepad.controller.ResourceController; import org.jcnc.jnotepad.controller.config.PluginConfigController; import org.jcnc.jnotepad.controller.manager.Controller; import org.jcnc.jnotepad.plugin.manager.PluginManager; +import org.jcnc.jnotepad.util.LogUtil; import org.jcnc.jnotepad.util.UiUtil; import org.jcnc.jnotepad.views.manager.RootManager; import org.jcnc.jnotepad.views.manager.SidebarToolBarManager; import org.jcnc.jnotepad.views.manager.TopMenuBarManager; +import org.slf4j.Logger; import java.util.List; import java.util.Objects; @@ -31,6 +34,7 @@ import java.util.concurrent.ExecutorService; * @author gewuyou */ public class ApplicationManager { + Logger logger = LogUtil.getLogger(this.getClass()); private static final ApplicationManager INSTANCE = new ApplicationManager(); /** * 线程池 @@ -180,4 +184,9 @@ public class ApplicationManager { public void setPrimaryStage(Stage primaryStage) { this.primaryStage = primaryStage; } + + public void stopApplication() { + Platform.exit(); +// application.stop(); + } } diff --git a/src/main/java/org/jcnc/jnotepad/model/entity/PluginDescriptor.java b/src/main/java/org/jcnc/jnotepad/model/entity/PluginDescriptor.java index af34454..b7e4f71 100644 --- a/src/main/java/org/jcnc/jnotepad/model/entity/PluginDescriptor.java +++ b/src/main/java/org/jcnc/jnotepad/model/entity/PluginDescriptor.java @@ -3,6 +3,8 @@ package org.jcnc.jnotepad.model.entity; import com.fasterxml.jackson.annotation.JsonIgnore; import org.jcnc.jnotepad.plugin.interfaces.Plugin; +import java.util.Objects; + /** * 插件信息 * @@ -241,4 +243,37 @@ public class PluginDescriptor { public void setId(String id) { this.id = id; } + + @Override + public int hashCode() { + return Objects.hash(id, name, version, enabled, author, category, icon, size, description, detailedIntroduction, log, pluginUrl, mainClass, assetFolder, readMe, score, plugin); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + PluginDescriptor other = (PluginDescriptor) obj; + return Objects.equals(id, other.id) && + Objects.equals(name, other.name) && + Objects.equals(version, other.version) && + enabled == other.enabled && + Objects.equals(author, other.author) && + Objects.equals(category, other.category) && + Objects.equals(icon, other.icon) && + Objects.equals(size, other.size) && + Objects.equals(description, other.description) && + Objects.equals(detailedIntroduction, other.detailedIntroduction) && + Objects.equals(log, other.log) && + Objects.equals(pluginUrl, other.pluginUrl) && + Objects.equals(mainClass, other.mainClass) && + Objects.equals(assetFolder, other.assetFolder) && + Objects.equals(readMe, other.readMe) && + Objects.equals(score, other.score) && + Objects.equals(plugin, other.plugin); + } } diff --git a/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java b/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java index 1d10825..b72fcb7 100644 --- a/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java +++ b/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java @@ -1,9 +1,11 @@ package org.jcnc.jnotepad.plugin.manager; +import org.jcnc.jnotepad.app.manager.ApplicationManager; import org.jcnc.jnotepad.common.manager.ThreadPoolManager; import org.jcnc.jnotepad.controller.config.PluginConfigController; import org.jcnc.jnotepad.model.entity.PluginDescriptor; import org.jcnc.jnotepad.util.LogUtil; +import org.jcnc.jnotepad.util.PopUpUtil; import org.slf4j.Logger; import java.io.File; @@ -67,29 +69,34 @@ public class PluginManager { */ public void unloadPlugin(PluginDescriptor pluginDescriptor) { // 删除集合中的插件信息 - pluginDescriptors.remove(pluginDescriptor); - PluginConfigController instance = PluginConfigController.getInstance(); - instance.getConfig().getPlugins().remove(pluginDescriptor); - // 刷新配置 - instance.writeConfig(); - // 删除本地插件jar包 - Path plungsPath = instance.getPlungsPath(); - try (Stream pathStream = Files.walk(plungsPath)) { - pathStream.filter(path -> path.toString().endsWith(".jar")).forEach(path -> { - try { - File pluginJar = new File(path.toString()); - PluginDescriptor temp = readPlugin(pluginJar); - if ((temp.getName() + temp.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor())) { - Files.delete(pluginJar.toPath()); + ThreadPoolManager.getThreadPool().submit(() -> { + // 移除插件管理类中的插件描述类 + pluginDescriptors.remove(pluginDescriptor); + // 移除插件配置文件类中的插件描述类 + PluginConfigController instance = PluginConfigController.getInstance(); + instance.getConfig().getPlugins().remove(pluginDescriptor); + + // 刷新配置 + instance.writeConfig(); + // 删除本地插件jar包 + Path plungsPath = instance.getPlungsPath(); + try (Stream pathStream = Files.walk(plungsPath)) { + pathStream.filter(path -> path.toString().endsWith(".jar")).forEach(path -> { + try { + File pluginJar = new File(path.toString()); + PluginDescriptor temp = readPlugin(pluginJar); + if ((temp.getName() + temp.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor())) { + Files.delete(pluginJar.toPath()); + } + } catch (IOException e) { + logger.error(e.getMessage(), e); } - } catch (IOException e) { - logger.error(e.getMessage(), e); - } - }); - } catch (Exception e) { - logger.error(e.getMessage(), e); - } - ThreadPoolManager.threadContSelfSubtracting(); + }); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + ThreadPoolManager.threadContSelfSubtracting(); + }); } /** @@ -166,10 +173,6 @@ public class PluginManager { return temporaryPluginDescriptors; } - public void setTemporaryPluginDescriptors(List temporaryPluginDescriptors) { - this.temporaryPluginDescriptors = temporaryPluginDescriptors; - } - /** * 启用插件 * @@ -183,15 +186,28 @@ public class PluginManager { * 保存插件设置并退出 */ public void saveAndExitSettings() { - pluginDescriptors = temporaryPluginDescriptors; + settingsChange(); clearTemporarySettings(); } + private void settingsChange() { + boolean equals = temporaryPluginDescriptors.equals(pluginDescriptors); + if (!equals) { + pluginDescriptors = temporaryPluginDescriptors; + PopUpUtil.questionAlert("更改", "程序与插件更新", "请重启程序以应用插件中的更改!", + appDialog -> { + appDialog.close(); + // 执行关闭操作 + ApplicationManager.getInstance().stopApplication(); + }, null); + } + } + /** * 保存插件设置但不退出 */ public void saveNotExitSettings() { - pluginDescriptors = temporaryPluginDescriptors; + settingsChange(); } /** diff --git a/src/main/java/org/jcnc/jnotepad/ui/dialog/AppDialog.java b/src/main/java/org/jcnc/jnotepad/ui/dialog/AppDialog.java index b916b9c..fad47f3 100644 --- a/src/main/java/org/jcnc/jnotepad/ui/dialog/AppDialog.java +++ b/src/main/java/org/jcnc/jnotepad/ui/dialog/AppDialog.java @@ -7,6 +7,7 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.stage.Stage; +import org.jcnc.jnotepad.ui.dialog.interfaces.DialogButtonAction; /** * 应用对话框 @@ -53,8 +54,8 @@ public class AppDialog extends Stage { // 自定义文本 Label customTextLabel = new Label(builder.getCustomText()); - Button confirmButton = createButton(builder.getLeftBtnText(), builder.getLeftBtnAction()::handleAction); - Button cancelButton = createButton(builder.getRightBtnText(), builder.getRightBtnAction()::handleAction); + Button confirmButton = createButton(builder.getLeftBtnText(), builder.getLeftBtnAction()); + Button cancelButton = createButton(builder.getRightBtnText(), builder.getRightBtnAction()); HBox hBox = new HBox(builder.getHBoxSpacing(), confirmButton, cancelButton); hBox.setAlignment(builder.getHboxPos()); @@ -75,9 +76,9 @@ public class AppDialog extends Stage { * @param action 按钮点击时的操作 * @return Button 按钮控件 */ - private Button createButton(String text, Runnable action) { + private Button createButton(String text, DialogButtonAction action) { Button button = new Button(text); - button.setOnAction(e -> action.run()); + button.setOnAction(e -> action.handleAction(this)); return button; } } diff --git a/src/main/java/org/jcnc/jnotepad/ui/dialog/AppDialogBuilder.java b/src/main/java/org/jcnc/jnotepad/ui/dialog/AppDialogBuilder.java index fcb0616..47ebc8e 100644 --- a/src/main/java/org/jcnc/jnotepad/ui/dialog/AppDialogBuilder.java +++ b/src/main/java/org/jcnc/jnotepad/ui/dialog/AppDialogBuilder.java @@ -4,6 +4,7 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.image.Image; import javafx.stage.Modality; +import javafx.stage.Stage; import org.jcnc.jnotepad.model.enums.DialogType; import org.jcnc.jnotepad.ui.dialog.interfaces.DialogButtonAction; import org.jcnc.jnotepad.util.UiUtil; @@ -24,9 +25,9 @@ public class AppDialogBuilder { private double height = 150; private FontIcon icon; - private DialogButtonAction leftBtnAction = () -> appDialog.close(); + private DialogButtonAction leftBtnAction = Stage::close; - private DialogButtonAction rightBtnAction = () -> appDialog.close(); + private DialogButtonAction rightBtnAction = Stage::close; private String leftBtnText = "确定"; diff --git a/src/main/java/org/jcnc/jnotepad/ui/dialog/interfaces/DialogButtonAction.java b/src/main/java/org/jcnc/jnotepad/ui/dialog/interfaces/DialogButtonAction.java index e7a4d9a..3942f7a 100644 --- a/src/main/java/org/jcnc/jnotepad/ui/dialog/interfaces/DialogButtonAction.java +++ b/src/main/java/org/jcnc/jnotepad/ui/dialog/interfaces/DialogButtonAction.java @@ -1,5 +1,7 @@ package org.jcnc.jnotepad.ui.dialog.interfaces; +import org.jcnc.jnotepad.ui.dialog.AppDialog; + /** * 对话框按钮点击事件接口 * @@ -8,10 +10,10 @@ package org.jcnc.jnotepad.ui.dialog.interfaces; public interface DialogButtonAction { /** * 处理按钮的操作。子类必须实现此方法以定义按钮的行为 - * + * @param appDialog 对话框 * @apiNote * @since 2023/9/3 22:53 */ - void handleAction(); + void handleAction(AppDialog appDialog); } diff --git a/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java b/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java index d33a8f4..a66d7bd 100644 --- a/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java +++ b/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java @@ -26,8 +26,11 @@ import org.commonmark.parser.Parser; import org.commonmark.renderer.html.HtmlRenderer; import org.jcnc.jnotepad.model.entity.PluginDescriptor; import org.jcnc.jnotepad.plugin.manager.PluginManager; +import org.jcnc.jnotepad.ui.dialog.AppDialog; +import org.jcnc.jnotepad.ui.dialog.interfaces.DialogButtonAction; import org.jcnc.jnotepad.ui.module.CustomSetButton; import org.jcnc.jnotepad.util.LogUtil; +import org.jcnc.jnotepad.util.PopUpUtil; import org.slf4j.Logger; import java.awt.*; @@ -149,6 +152,10 @@ public class PluginManagementPane extends BorderPane { bottomBox.setAlignment(Pos.CENTER_RIGHT); bottomBox.setStyle("-fx-background-color: rgba(43,43,43,0.12);"); bottomBox.setPadding(new Insets(7, 15, 7, 0)); + Button applicationButton = new Button(" 应用 "); + applicationButton.getStyleClass().addAll(Styles.SMALL); + applicationButton.setOnAction(event -> pluginManager.saveNotExitSettings()); + Button confirmButton = new Button(" 确认 "); confirmButton.setTextFill(Color.WHITE); confirmButton.getStyleClass().addAll(Styles.SMALL); @@ -168,9 +175,7 @@ public class PluginManagementPane extends BorderPane { stage.close(); }); cancelButton.getStyleClass().addAll(Styles.SMALL); - Button applicationButton = new Button(" 应用 "); - applicationButton.getStyleClass().addAll(Styles.SMALL); - applicationButton.setOnAction(event -> pluginManager.saveNotExitSettings()); + bottomBox.getChildren().addAll(confirmButton, cancelButton, applicationButton); this.setBottom(bottomBox); @@ -228,11 +233,11 @@ public class PluginManagementPane extends BorderPane { tile.setAction(toggleSwitch); tile.setActionHandler(() -> { customSplitPane.setRightContent(tileContentMap.get(tile)); - logger.info("点击了" + tile); + logger.info("点击了{}", tile); }); // 创建专属的customSplitPane内容 - var content = createCustomSplitPaneContent(pluginDescriptor, toggleSwitch); + var content = createCustomSplitPaneContent(pluginDescriptor, toggleSwitch, tile); // 将内容与Tile关联起来 tileContentMap.put(tile, content); @@ -245,7 +250,7 @@ public class PluginManagementPane extends BorderPane { * * @return 创建的CustomSplitPane内容 */ - private Node createCustomSplitPaneContent(PluginDescriptor pluginDescriptor, ToggleSwitch toggleSwitch) { + private Node createCustomSplitPaneContent(PluginDescriptor pluginDescriptor, ToggleSwitch toggleSwitch, Tile tile) { VBox content = new VBox(8); content.setPadding(new Insets(10)); var titleLabel = new Text(pluginDescriptor.getName()); @@ -258,17 +263,30 @@ public class PluginManagementPane extends BorderPane { var uninstallItem = new MenuItem("卸载"); + var state = new SplitMenuButton(uninstallItem); toggleSwitch.setSelected(pluginDescriptor.isEnabled()); BooleanProperty booleanProperty = toggleSwitch.selectedProperty(); state.textProperty().bind(Bindings.when(booleanProperty).then("禁用").otherwise("启用")); + + uninstallItem.setOnAction(event -> PopUpUtil.warningAlert("卸载", "确定要卸载" + pluginDescriptor.getName() + "吗?", "此操作无法撤销!", new DialogButtonAction() { + @Override + public void handleAction(AppDialog dialog) { + pluginManager.unloadPlugin(pluginDescriptor); + state.setDisable(true); + toggleSwitch.setDisable(true); + dialog.close(); + } + }, null)); state.getStyleClass().addAll(Styles.ACCENT); state.setPrefWidth(80); toggleSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> { if (Boolean.TRUE.equals(newValue)) { pluginManager.enablePlugIn(pluginDescriptor); + customSplitPane.setRightContent(tileContentMap.get(tile)); } else { pluginManager.disablePlugIn(pluginDescriptor); + customSplitPane.setRightContent(tileContentMap.get(tile)); } }); state.setOnAction(event -> { diff --git a/src/main/java/org/jcnc/jnotepad/views/manager/TopMenuBarManager.java b/src/main/java/org/jcnc/jnotepad/views/manager/TopMenuBarManager.java index ccd8485..224153d 100644 --- a/src/main/java/org/jcnc/jnotepad/views/manager/TopMenuBarManager.java +++ b/src/main/java/org/jcnc/jnotepad/views/manager/TopMenuBarManager.java @@ -357,6 +357,4 @@ public class TopMenuBarManager { }); topMenu.add(menu); } - - }