!116 完善插件逻辑

Merge pull request !116 from 格物方能致知/develop
This commit is contained in:
格物方能致知 2023-09-24 10:40:38 +00:00 committed by Gitee
commit c084fe34cb
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
8 changed files with 124 additions and 44 deletions

View File

@ -2,6 +2,7 @@ package org.jcnc.jnotepad.app.manager;
import atlantafx.base.theme.PrimerLight; import atlantafx.base.theme.PrimerLight;
import javafx.application.Application; import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.stage.Stage; 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.config.PluginConfigController;
import org.jcnc.jnotepad.controller.manager.Controller; import org.jcnc.jnotepad.controller.manager.Controller;
import org.jcnc.jnotepad.plugin.manager.PluginManager; import org.jcnc.jnotepad.plugin.manager.PluginManager;
import org.jcnc.jnotepad.util.LogUtil;
import org.jcnc.jnotepad.util.UiUtil; import org.jcnc.jnotepad.util.UiUtil;
import org.jcnc.jnotepad.views.manager.RootManager; import org.jcnc.jnotepad.views.manager.RootManager;
import org.jcnc.jnotepad.views.manager.SidebarToolBarManager; import org.jcnc.jnotepad.views.manager.SidebarToolBarManager;
import org.jcnc.jnotepad.views.manager.TopMenuBarManager; import org.jcnc.jnotepad.views.manager.TopMenuBarManager;
import org.slf4j.Logger;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -31,6 +34,7 @@ import java.util.concurrent.ExecutorService;
* @author gewuyou * @author gewuyou
*/ */
public class ApplicationManager { public class ApplicationManager {
Logger logger = LogUtil.getLogger(this.getClass());
private static final ApplicationManager INSTANCE = new ApplicationManager(); private static final ApplicationManager INSTANCE = new ApplicationManager();
/** /**
* 线程池 * 线程池
@ -180,4 +184,9 @@ public class ApplicationManager {
public void setPrimaryStage(Stage primaryStage) { public void setPrimaryStage(Stage primaryStage) {
this.primaryStage = primaryStage; this.primaryStage = primaryStage;
} }
public void stopApplication() {
Platform.exit();
// application.stop();
}
} }

View File

@ -3,6 +3,8 @@ package org.jcnc.jnotepad.model.entity;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import org.jcnc.jnotepad.plugin.interfaces.Plugin; import org.jcnc.jnotepad.plugin.interfaces.Plugin;
import java.util.Objects;
/** /**
* 插件信息 * 插件信息
* *
@ -241,4 +243,37 @@ public class PluginDescriptor {
public void setId(String id) { public void setId(String id) {
this.id = 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);
}
} }

View File

@ -1,9 +1,11 @@
package org.jcnc.jnotepad.plugin.manager; package org.jcnc.jnotepad.plugin.manager;
import org.jcnc.jnotepad.app.manager.ApplicationManager;
import org.jcnc.jnotepad.common.manager.ThreadPoolManager; import org.jcnc.jnotepad.common.manager.ThreadPoolManager;
import org.jcnc.jnotepad.controller.config.PluginConfigController; import org.jcnc.jnotepad.controller.config.PluginConfigController;
import org.jcnc.jnotepad.model.entity.PluginDescriptor; import org.jcnc.jnotepad.model.entity.PluginDescriptor;
import org.jcnc.jnotepad.util.LogUtil; import org.jcnc.jnotepad.util.LogUtil;
import org.jcnc.jnotepad.util.PopUpUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.io.File; import java.io.File;
@ -67,29 +69,34 @@ public class PluginManager {
*/ */
public void unloadPlugin(PluginDescriptor pluginDescriptor) { public void unloadPlugin(PluginDescriptor pluginDescriptor) {
// 删除集合中的插件信息 // 删除集合中的插件信息
pluginDescriptors.remove(pluginDescriptor); ThreadPoolManager.getThreadPool().submit(() -> {
PluginConfigController instance = PluginConfigController.getInstance(); // 移除插件管理类中的插件描述类
instance.getConfig().getPlugins().remove(pluginDescriptor); pluginDescriptors.remove(pluginDescriptor);
// 刷新配置 // 移除插件配置文件类中的插件描述类
instance.writeConfig(); PluginConfigController instance = PluginConfigController.getInstance();
// 删除本地插件jar包 instance.getConfig().getPlugins().remove(pluginDescriptor);
Path plungsPath = instance.getPlungsPath();
try (Stream<Path> pathStream = Files.walk(plungsPath)) { // 刷新配置
pathStream.filter(path -> path.toString().endsWith(".jar")).forEach(path -> { instance.writeConfig();
try { // 删除本地插件jar包
File pluginJar = new File(path.toString()); Path plungsPath = instance.getPlungsPath();
PluginDescriptor temp = readPlugin(pluginJar); try (Stream<Path> pathStream = Files.walk(plungsPath)) {
if ((temp.getName() + temp.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor())) { pathStream.filter(path -> path.toString().endsWith(".jar")).forEach(path -> {
Files.delete(pluginJar.toPath()); 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);
}); }
} catch (Exception e) { ThreadPoolManager.threadContSelfSubtracting();
logger.error(e.getMessage(), e); });
}
ThreadPoolManager.threadContSelfSubtracting();
} }
/** /**
@ -166,10 +173,6 @@ public class PluginManager {
return temporaryPluginDescriptors; return temporaryPluginDescriptors;
} }
public void setTemporaryPluginDescriptors(List<PluginDescriptor> temporaryPluginDescriptors) {
this.temporaryPluginDescriptors = temporaryPluginDescriptors;
}
/** /**
* 启用插件 * 启用插件
* *
@ -183,15 +186,28 @@ public class PluginManager {
* 保存插件设置并退出 * 保存插件设置并退出
*/ */
public void saveAndExitSettings() { public void saveAndExitSettings() {
pluginDescriptors = temporaryPluginDescriptors; settingsChange();
clearTemporarySettings(); 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() { public void saveNotExitSettings() {
pluginDescriptors = temporaryPluginDescriptors; settingsChange();
} }
/** /**

View File

@ -7,6 +7,7 @@ import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.stage.Stage; 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()); Label customTextLabel = new Label(builder.getCustomText());
Button confirmButton = createButton(builder.getLeftBtnText(), builder.getLeftBtnAction()::handleAction); Button confirmButton = createButton(builder.getLeftBtnText(), builder.getLeftBtnAction());
Button cancelButton = createButton(builder.getRightBtnText(), builder.getRightBtnAction()::handleAction); Button cancelButton = createButton(builder.getRightBtnText(), builder.getRightBtnAction());
HBox hBox = new HBox(builder.getHBoxSpacing(), confirmButton, cancelButton); HBox hBox = new HBox(builder.getHBoxSpacing(), confirmButton, cancelButton);
hBox.setAlignment(builder.getHboxPos()); hBox.setAlignment(builder.getHboxPos());
@ -75,9 +76,9 @@ public class AppDialog extends Stage {
* @param action 按钮点击时的操作 * @param action 按钮点击时的操作
* @return Button 按钮控件 * @return Button 按钮控件
*/ */
private Button createButton(String text, Runnable action) { private Button createButton(String text, DialogButtonAction action) {
Button button = new Button(text); Button button = new Button(text);
button.setOnAction(e -> action.run()); button.setOnAction(e -> action.handleAction(this));
return button; return button;
} }
} }

View File

@ -4,6 +4,7 @@ import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.stage.Modality; import javafx.stage.Modality;
import javafx.stage.Stage;
import org.jcnc.jnotepad.model.enums.DialogType; import org.jcnc.jnotepad.model.enums.DialogType;
import org.jcnc.jnotepad.ui.dialog.interfaces.DialogButtonAction; import org.jcnc.jnotepad.ui.dialog.interfaces.DialogButtonAction;
import org.jcnc.jnotepad.util.UiUtil; import org.jcnc.jnotepad.util.UiUtil;
@ -24,9 +25,9 @@ public class AppDialogBuilder {
private double height = 150; private double height = 150;
private FontIcon icon; 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 = "确定"; private String leftBtnText = "确定";

View File

@ -1,5 +1,7 @@
package org.jcnc.jnotepad.ui.dialog.interfaces; 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 { public interface DialogButtonAction {
/** /**
* 处理按钮的操作子类必须实现此方法以定义按钮的行为 * 处理按钮的操作子类必须实现此方法以定义按钮的行为
* * @param appDialog 对话框
* @apiNote * @apiNote
* @since 2023/9/3 22:53 * @since 2023/9/3 22:53
*/ */
void handleAction(); void handleAction(AppDialog appDialog);
} }

View File

@ -26,8 +26,11 @@ import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer; import org.commonmark.renderer.html.HtmlRenderer;
import org.jcnc.jnotepad.model.entity.PluginDescriptor; import org.jcnc.jnotepad.model.entity.PluginDescriptor;
import org.jcnc.jnotepad.plugin.manager.PluginManager; 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.ui.module.CustomSetButton;
import org.jcnc.jnotepad.util.LogUtil; import org.jcnc.jnotepad.util.LogUtil;
import org.jcnc.jnotepad.util.PopUpUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.awt.*; import java.awt.*;
@ -149,6 +152,10 @@ public class PluginManagementPane extends BorderPane {
bottomBox.setAlignment(Pos.CENTER_RIGHT); bottomBox.setAlignment(Pos.CENTER_RIGHT);
bottomBox.setStyle("-fx-background-color: rgba(43,43,43,0.12);"); bottomBox.setStyle("-fx-background-color: rgba(43,43,43,0.12);");
bottomBox.setPadding(new Insets(7, 15, 7, 0)); 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(" 确认 "); Button confirmButton = new Button(" 确认 ");
confirmButton.setTextFill(Color.WHITE); confirmButton.setTextFill(Color.WHITE);
confirmButton.getStyleClass().addAll(Styles.SMALL); confirmButton.getStyleClass().addAll(Styles.SMALL);
@ -168,9 +175,7 @@ public class PluginManagementPane extends BorderPane {
stage.close(); stage.close();
}); });
cancelButton.getStyleClass().addAll(Styles.SMALL); 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); bottomBox.getChildren().addAll(confirmButton, cancelButton, applicationButton);
this.setBottom(bottomBox); this.setBottom(bottomBox);
@ -228,11 +233,11 @@ public class PluginManagementPane extends BorderPane {
tile.setAction(toggleSwitch); tile.setAction(toggleSwitch);
tile.setActionHandler(() -> { tile.setActionHandler(() -> {
customSplitPane.setRightContent(tileContentMap.get(tile)); customSplitPane.setRightContent(tileContentMap.get(tile));
logger.info("点击了" + tile); logger.info("点击了{}", tile);
}); });
// 创建专属的customSplitPane内容 // 创建专属的customSplitPane内容
var content = createCustomSplitPaneContent(pluginDescriptor, toggleSwitch); var content = createCustomSplitPaneContent(pluginDescriptor, toggleSwitch, tile);
// 将内容与Tile关联起来 // 将内容与Tile关联起来
tileContentMap.put(tile, content); tileContentMap.put(tile, content);
@ -245,7 +250,7 @@ public class PluginManagementPane extends BorderPane {
* *
* @return 创建的CustomSplitPane内容 * @return 创建的CustomSplitPane内容
*/ */
private Node createCustomSplitPaneContent(PluginDescriptor pluginDescriptor, ToggleSwitch toggleSwitch) { private Node createCustomSplitPaneContent(PluginDescriptor pluginDescriptor, ToggleSwitch toggleSwitch, Tile tile) {
VBox content = new VBox(8); VBox content = new VBox(8);
content.setPadding(new Insets(10)); content.setPadding(new Insets(10));
var titleLabel = new Text(pluginDescriptor.getName()); var titleLabel = new Text(pluginDescriptor.getName());
@ -258,17 +263,30 @@ public class PluginManagementPane extends BorderPane {
var uninstallItem = new MenuItem("卸载"); var uninstallItem = new MenuItem("卸载");
var state = new SplitMenuButton(uninstallItem); var state = new SplitMenuButton(uninstallItem);
toggleSwitch.setSelected(pluginDescriptor.isEnabled()); toggleSwitch.setSelected(pluginDescriptor.isEnabled());
BooleanProperty booleanProperty = toggleSwitch.selectedProperty(); BooleanProperty booleanProperty = toggleSwitch.selectedProperty();
state.textProperty().bind(Bindings.when(booleanProperty).then("禁用").otherwise("启用")); 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.getStyleClass().addAll(Styles.ACCENT);
state.setPrefWidth(80); state.setPrefWidth(80);
toggleSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> { toggleSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (Boolean.TRUE.equals(newValue)) { if (Boolean.TRUE.equals(newValue)) {
pluginManager.enablePlugIn(pluginDescriptor); pluginManager.enablePlugIn(pluginDescriptor);
customSplitPane.setRightContent(tileContentMap.get(tile));
} else { } else {
pluginManager.disablePlugIn(pluginDescriptor); pluginManager.disablePlugIn(pluginDescriptor);
customSplitPane.setRightContent(tileContentMap.get(tile));
} }
}); });
state.setOnAction(event -> { state.setOnAction(event -> {

View File

@ -357,6 +357,4 @@ public class TopMenuBarManager {
}); });
topMenu.add(menu); topMenu.add(menu);
} }
} }