!103 ♻️ 重构插件类加载逻辑 修复 导入插件错误 添加工具栏插件支持

Merge pull request !103 from 格物方能致知/develop
This commit is contained in:
Luke 2023-09-19 07:08:50 +00:00 committed by Gitee
commit 6398b2f7fe
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
20 changed files with 361 additions and 376 deletions

View File

@ -71,7 +71,6 @@ JNotepad使用Java语言编写并基于JavaFX框架开发具有良好的
[docs-url]: https://gitee.com/jcnc-org/docs [docs-url]: https://gitee.com/jcnc-org/docs
- [下载][gitee-download] - [下载][gitee-download]
2. Linux/MacOS 平台,查看入门指南 2. Linux/MacOS 平台,查看入门指南
@ -111,13 +110,12 @@ JNotepad使用Java语言编写并基于JavaFX框架开发具有良好的
- `插件 > 增加插件`(管理插件系统,待完善)。 - `插件 > 增加插件`(管理插件系统,待完善)。
## 依赖项 ## 依赖项
POM文件中的全部依赖项 POM文件中的全部依赖项
| 组ID | 工件ID | 版本 | 功能描述 | | 组ID | 工件ID | 版本 | 功能描述 |
|--------------------------------|------------------------------|--------|------------------------------------------------| |----------------------------|----------------------------|--------|--------------------------------------------------------------|
| org.kordamp.ikonli | ikonli-javafx | 12.3.1 | 提供JavaFX应用程序中的图标集成。 | | org.kordamp.ikonli | ikonli-javafx | 12.3.1 | 提供JavaFX应用程序中的图标集成。 |
| org.kordamp.ikonli | ikonli-antdesignicons-pack | 12.3.1 | 包含Ant Design图标集的Ikonli图标包。 | | org.kordamp.ikonli | ikonli-antdesignicons-pack | 12.3.1 | 包含Ant Design图标集的Ikonli图标包。 |
| io.github.mkpaz | atlantafx-base | 2.0.1 | 提供Atlantafx库的基本功能。 | | io.github.mkpaz | atlantafx-base | 2.0.1 | 提供Atlantafx库的基本功能。 |
@ -129,7 +127,6 @@ POM文件中的全部依赖项
| ch.qos.logback | logback-classic | 1.4.11 | Logback的经典模块提供日志记录功能。 | | ch.qos.logback | logback-classic | 1.4.11 | Logback的经典模块提供日志记录功能。 |
| com.ibm.icu | icu4j | 73.2 | ICUInternational Components for Unicode用于处理Unicode字符和文本。 | | com.ibm.icu | icu4j | 73.2 | ICUInternational Components for Unicode用于处理Unicode字符和文本。 |
## 软件运行截图 ## 软件运行截图
- Windows 平台 - Windows 平台

View File

@ -40,4 +40,5 @@ jar uf libs/icu4j-73.2.jar -C libs/tmpOut/com.ibm.icu module-info.class
``` ```
## Reference ## Reference
1. [java_jlink_automatic_module_cannot_be_used_with_jlink](https://tacosteemers.com/articles/java_jlink_automatic_module_cannot_be_used_with_jlink.html) 1. [java_jlink_automatic_module_cannot_be_used_with_jlink](https://tacosteemers.com/articles/java_jlink_automatic_module_cannot_be_used_with_jlink.html)

View File

@ -31,11 +31,13 @@ module org.jcnc.jnotepad {
exports org.jcnc.jnotepad.common.interfaces; exports org.jcnc.jnotepad.common.interfaces;
opens org.jcnc.jnotepad.app.config; opens org.jcnc.jnotepad.app.config;
exports org.jcnc.jnotepad.plugin.interfaces; exports org.jcnc.jnotepad.plugin.interfaces;
exports org.jcnc.jnotepad.views.root.bottom.function;
exports org.jcnc.jnotepad.views.root.bottom.function.interfaces;
exports org.jcnc.jnotepad.ui.dialog; exports org.jcnc.jnotepad.ui.dialog;
exports org.jcnc.jnotepad.ui.dialog.interfaces; exports org.jcnc.jnotepad.ui.dialog.interfaces;
exports org.jcnc.jnotepad.ui.module;
exports org.jcnc.jnotepad.model.entity; exports org.jcnc.jnotepad.model.entity;
exports org.jcnc.jnotepad.views.root.bottom; exports org.jcnc.jnotepad.views.root.bottom;
exports org.jcnc.jnotepad.views.root.bottom.status; exports org.jcnc.jnotepad.views.root.bottom.status;
exports org.jcnc.jnotepad.views.root.bottom.cmd;
} }

View File

@ -83,12 +83,14 @@ public class LunchApp extends Application {
// 1. 加载语言 // 1. 加载语言
LocalizationController.initLocal(); LocalizationController.initLocal();
// 2. 加载资源
// 2. 加载组件 ResourceController.getInstance().loadResources();
// 3. 初始化插件
PluginManager.getInstance().initializePlugins();
// 3. 加载组件
ViewManager viewManager = ViewManager.getInstance(SCENE); ViewManager viewManager = ViewManager.getInstance(SCENE);
viewManager.initScreen(SCENE); viewManager.initScreen(SCENE);
// 3. 加载资源
ResourceController.getInstance().loadResources();
// 使用线程池加载关联文件并创建文本区域 // 使用线程池加载关联文件并创建文本区域
List<String> rawParameters = getParameters().getRaw(); List<String> rawParameters = getParameters().getRaw();
Controller.getInstance().openAssociatedFileAndCreateTextArea(rawParameters); Controller.getInstance().openAssociatedFileAndCreateTextArea(rawParameters);

View File

@ -1,6 +1,6 @@
package org.jcnc.jnotepad.app.config; package org.jcnc.jnotepad.app.config;
import org.jcnc.jnotepad.model.entity.PluginInfo; import org.jcnc.jnotepad.model.entity.PluginDescriptor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -11,7 +11,7 @@ import java.util.List;
* @author gewuyou * @author gewuyou
*/ */
public class PluginConfig { public class PluginConfig {
private List<PluginInfo> plugins; private List<PluginDescriptor> plugins;
/** /**
* 生成默认的插件配置文件 * 生成默认的插件配置文件
@ -26,11 +26,11 @@ public class PluginConfig {
return pluginConfig; return pluginConfig;
} }
public List<PluginInfo> getPlugins() { public List<PluginDescriptor> getPlugins() {
return plugins; return plugins;
} }
public void setPlugins(List<PluginInfo> plugins) { public void setPlugins(List<PluginDescriptor> plugins) {
this.plugins = plugins; this.plugins = plugins;
} }
} }

View File

@ -8,7 +8,7 @@ import org.jcnc.jnotepad.plugin.interfaces.Plugin;
* *
* @author gewuyou * @author gewuyou
*/ */
public class PluginInfo { public class PluginDescriptor {
/** /**
* 插件名称 * 插件名称
*/ */

View File

@ -2,7 +2,7 @@ package org.jcnc.jnotepad.plugin;
import org.jcnc.jnotepad.controller.config.PluginConfigController; import org.jcnc.jnotepad.controller.config.PluginConfigController;
import org.jcnc.jnotepad.exception.AppException; import org.jcnc.jnotepad.exception.AppException;
import org.jcnc.jnotepad.model.entity.PluginInfo; import org.jcnc.jnotepad.model.entity.PluginDescriptor;
import org.jcnc.jnotepad.plugin.interfaces.Plugin; import org.jcnc.jnotepad.plugin.interfaces.Plugin;
import org.jcnc.jnotepad.util.JsonUtil; import org.jcnc.jnotepad.util.JsonUtil;
import org.jcnc.jnotepad.util.LogUtil; import org.jcnc.jnotepad.util.LogUtil;
@ -12,10 +12,8 @@ import java.io.*;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.ArrayList; import java.util.*;
import java.util.Iterator; import java.util.jar.JarEntry;
import java.util.List;
import java.util.Map;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
@ -29,11 +27,11 @@ public class PluginLoader {
Logger logger = LogUtil.getLogger(this.getClass()); Logger logger = LogUtil.getLogger(this.getClass());
/** /**
* 从插件jar包中读取 json 文件到 PluginInfo * 从插件jar包中读取 json 文件到 PluginDescriptor
* *
* @param pluginJar jar * @param pluginJar jar
*/ */
public static PluginInfo readPlugin(File pluginJar) throws IOException { public static PluginDescriptor readPlugin(File pluginJar) throws IOException {
InputStream is; InputStream is;
StringBuilder sb; StringBuilder sb;
try (JarFile jarFile = new JarFile(pluginJar)) { try (JarFile jarFile = new JarFile(pluginJar)) {
@ -48,7 +46,7 @@ public class PluginLoader {
} }
} }
} }
return JsonUtil.OBJECT_MAPPER.readValue(sb.toString(), PluginInfo.class); return JsonUtil.OBJECT_MAPPER.readValue(sb.toString(), PluginDescriptor.class);
} }
public static PluginLoader getInstance() { public static PluginLoader getInstance() {
@ -58,38 +56,38 @@ public class PluginLoader {
/** /**
* 检查插件 * 检查插件
* *
* @param configPluginInfos 配置文件插件信息 * @param configPluginDescriptors 配置文件插件信息
* @param pluginInfo 插件信息类 * @param pluginDescriptor 插件信息类
* @param pluginInfos 插件信息集合 * @param pluginDescriptors 插件信息集合
* @return boolean 是否检查通过 * @return boolean 是否检查通过
* @apiNote * @apiNote
* @since 2023/9/16 14:04 * @since 2023/9/16 14:04
*/ */
private static boolean checkPlugin(List<PluginInfo> configPluginInfos, PluginInfo pluginInfo, List<PluginInfo> pluginInfos) { private static boolean checkPlugin(List<PluginDescriptor> configPluginDescriptors, PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors) {
// 如果应用程序配置文件为空则默认插件被禁用 // 如果应用程序配置文件为空则默认插件被禁用
if (configPluginInfos.isEmpty()) { if (configPluginDescriptors.isEmpty()) {
return disabledByDefault(configPluginInfos, pluginInfo, pluginInfos); return disabledByDefault(configPluginDescriptors, pluginDescriptor, pluginDescriptors);
} }
// 如果应用程序配置文件中该插件禁用则不加载 // 如果应用程序配置文件中该插件禁用则不加载
for (PluginInfo configPluginInfo : configPluginInfos) { for (PluginDescriptor configPluginDescriptor : configPluginDescriptors) {
if (disableDoNotLoad(pluginInfo, pluginInfos, configPluginInfo)) { if (disableDoNotLoad(pluginDescriptor, pluginDescriptors, configPluginDescriptor)) {
return true; return true;
} }
} }
// 判断该插件是否已经加载 // 判断该插件是否已经加载
return loaded(pluginInfo, pluginInfos); return loaded(pluginDescriptor, pluginDescriptors);
} }
private static boolean loaded(PluginInfo pluginInfo, List<PluginInfo> pluginInfos) { private static boolean loaded(PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors) {
Iterator<PluginInfo> iterator = pluginInfos.iterator(); Iterator<PluginDescriptor> iterator = pluginDescriptors.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
PluginInfo plugin = iterator.next(); PluginDescriptor plugin = iterator.next();
if ((plugin.getName() + plugin.getAuthor()).equals(pluginInfo.getName() + pluginInfo.getAuthor())) { if ((plugin.getName() + plugin.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor())) {
if (plugin.getVersion().equals(pluginInfo.getVersion())) { if (plugin.getVersion().equals(pluginDescriptor.getVersion())) {
return true; return true;
} }
// 如果当前插件版本更低则更新 // 如果当前插件版本更低则更新
if (plugin.getVersion().compareTo(pluginInfo.getVersion()) < 0) { if (plugin.getVersion().compareTo(pluginDescriptor.getVersion()) < 0) {
// 删除当前的插件 // 删除当前的插件
iterator.remove(); iterator.remove();
} else { } else {
@ -100,19 +98,19 @@ public class PluginLoader {
return false; return false;
} }
private static boolean disableDoNotLoad(PluginInfo pluginInfo, List<PluginInfo> pluginInfos, PluginInfo configPluginInfo) { private static boolean disableDoNotLoad(PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors, PluginDescriptor configPluginDescriptor) {
if ((configPluginInfo.getName() + configPluginInfo.getAuthor()).equals(pluginInfo.getName() + pluginInfo.getAuthor()) && !configPluginInfo.isEnabled()) { if ((configPluginDescriptor.getName() + configPluginDescriptor.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor()) && !configPluginDescriptor.isEnabled()) {
pluginInfo.setEnabled(false); pluginDescriptor.setEnabled(false);
pluginInfos.add(pluginInfo); pluginDescriptors.add(pluginDescriptor);
return true; return true;
} }
return false; return false;
} }
private static boolean disabledByDefault(List<PluginInfo> configPluginInfos, PluginInfo pluginInfo, List<PluginInfo> pluginInfos) { private static boolean disabledByDefault(List<PluginDescriptor> configPluginDescriptors, PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors) {
pluginInfo.setEnabled(false); pluginDescriptor.setEnabled(false);
pluginInfos.add(pluginInfo); pluginDescriptors.add(pluginDescriptor);
configPluginInfos.add(pluginInfo); configPluginDescriptors.add(pluginDescriptor);
PluginConfigController.getInstance().writeConfig(); PluginConfigController.getInstance().writeConfig();
return true; return true;
} }
@ -131,36 +129,32 @@ public class PluginLoader {
* 根据文件加载插件 * 根据文件加载插件
* *
* @param pluginJar 插件jar包 * @param pluginJar 插件jar包
* @param configPluginInfos 配置文件插件信息集合 * @param configPluginDescriptors 配置文件插件信息集合
* @apiNote * @apiNote
* @since 2023/9/16 14:05 * @since 2023/9/16 14:05
*/ */
public void loadPluginByFile(File pluginJar, List<PluginInfo> configPluginInfos) { public void loadPluginByFile(File pluginJar, List<PluginDescriptor> configPluginDescriptors) {
PluginManager pluginManager = PluginManager.getInstance(); PluginManager pluginManager = PluginManager.getInstance();
Map<String, List<String>> categories = pluginManager.getLoadedPluginsByCategory(); Map<String, List<String>> categories = pluginManager.getLoadedPluginsByCategory();
List<PluginInfo> pluginInfos = pluginManager.getPluginInfos(); List<PluginDescriptor> pluginDescriptors = pluginManager.getPluginInfos();
if (pluginJar.exists() && pluginJar.isFile()) { if (pluginJar.exists() && pluginJar.isFile()) {
try { try {
PluginInfo pluginInfo = readPlugin(pluginJar); PluginDescriptor pluginDescriptor = readPlugin(pluginJar);
// 检查插件状态 // 检查插件状态
if (checkPlugin(configPluginInfos, pluginInfo, pluginInfos)) { if (checkPlugin(configPluginDescriptors, pluginDescriptor, pluginDescriptors)) {
return; return;
} }
pluginInfo.setEnabled(true); pluginDescriptor.setEnabled(true);
pluginInfos.add(pluginInfo); pluginDescriptors.add(pluginDescriptor);
// 创建URLClassLoader以加载Jar文件中的类 // 创建URLClassLoader以加载Jar文件中的类
Class<?> pluginClass; Class<?> pluginClass = loaderJarFileClass(pluginJar, pluginDescriptor);
try (URLClassLoader classLoader = new URLClassLoader(new URL[]{pluginJar.toURI().toURL()})) {
logger.info("{}", pluginInfo.getMainClass());
pluginClass = classLoader.loadClass(pluginInfo.getMainClass());
}
if (pluginClass == null) { if (pluginClass == null) {
return; return;
} }
Plugin plugin; Plugin plugin;
plugin = (Plugin) pluginClass.getDeclaredConstructor().newInstance(); plugin = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
pluginInfo.setPlugin(plugin); pluginDescriptor.setPlugin(plugin);
categories.computeIfAbsent(pluginInfo.getCategory(), k -> new ArrayList<>()).add(pluginInfo.getName()); categories.computeIfAbsent(pluginDescriptor.getCategory(), k -> new ArrayList<>()).add(pluginDescriptor.getName());
} catch (IOException | InvocationTargetException | InstantiationException | IllegalAccessException e) { } catch (IOException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new AppException(e); throw new AppException(e);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
@ -170,7 +164,39 @@ public class PluginLoader {
} }
} else { } else {
LogUtil.getLogger(this.getClass()).info("PluginInfo file not found"); LogUtil.getLogger(this.getClass()).info("PluginDescriptor file not found");
}
}
/**
* 加载类中的class文件并返回插件主类
*
* @param pluginJar 插件jar包
* @param pluginDescriptor 插件描述
* @return java.lang.Class<?> 插件主类
* @apiNote
* @since 2023/9/19 14:00
*/
private Class<?> loaderJarFileClass(File pluginJar, PluginDescriptor pluginDescriptor) throws IOException, ClassNotFoundException {
Class<?> pluginClass;
try (
URLClassLoader classLoader = new URLClassLoader(new URL[]{pluginJar.toURI().toURL()});
JarFile jar = new JarFile(pluginJar)
) {
logger.info("{}", pluginDescriptor.getMainClass());
// 加载插件所需的依赖类
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
String className = entry.getName().replace("/", ".").replace(".class", "");
if (!pluginDescriptor.getMainClass().equals(className) && !"module-info".equals(className)) {
classLoader.loadClass(className);
} }
} }
} }
pluginClass = classLoader.loadClass(pluginDescriptor.getMainClass());
}
return pluginClass;
}
}

View File

@ -2,7 +2,7 @@ package org.jcnc.jnotepad.plugin;
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.PluginInfo; import org.jcnc.jnotepad.model.entity.PluginDescriptor;
import org.jcnc.jnotepad.util.LogUtil; import org.jcnc.jnotepad.util.LogUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -28,7 +28,6 @@ import static org.jcnc.jnotepad.plugin.PluginLoader.readPlugin;
*/ */
public class PluginManager { public class PluginManager {
private static final PluginManager INSTANCE = new PluginManager(); private static final PluginManager INSTANCE = new PluginManager();
Logger logger = LogUtil.getLogger(this.getClass());
/** /**
* 插件类别 * 插件类别
*/ */
@ -36,7 +35,8 @@ public class PluginManager {
/** /**
* 插件信息 * 插件信息
*/ */
private final List<PluginInfo> pluginInfos = new ArrayList<>(); private final List<PluginDescriptor> pluginDescriptors = new ArrayList<>();
Logger logger = LogUtil.getLogger(this.getClass());
private PluginManager() { private PluginManager() {
@ -50,14 +50,14 @@ public class PluginManager {
/** /**
* 卸载插件 * 卸载插件
* *
* @param pluginInfo 插件信息类 * @param pluginDescriptor 插件信息类
* @since 2023/9/11 12:28 * @since 2023/9/11 12:28
*/ */
public void unloadPlugin(PluginInfo pluginInfo) { public void unloadPlugin(PluginDescriptor pluginDescriptor) {
// 删除集合中的插件信息 // 删除集合中的插件信息
pluginInfos.remove(pluginInfo); pluginDescriptors.remove(pluginDescriptor);
PluginConfigController instance = PluginConfigController.getInstance(); PluginConfigController instance = PluginConfigController.getInstance();
instance.getConfig().getPlugins().remove(pluginInfo); instance.getConfig().getPlugins().remove(pluginDescriptor);
// 刷新配置 // 刷新配置
instance.writeConfig(); instance.writeConfig();
// 删除本地插件jar包 // 删除本地插件jar包
@ -66,8 +66,8 @@ public class PluginManager {
pathStream.filter(path -> path.toString().endsWith(".jar")).forEach(path -> { pathStream.filter(path -> path.toString().endsWith(".jar")).forEach(path -> {
try { try {
File pluginJar = new File(path.toString()); File pluginJar = new File(path.toString());
PluginInfo temp = readPlugin(pluginJar); PluginDescriptor temp = readPlugin(pluginJar);
if ((temp.getName() + temp.getAuthor()).equals(pluginInfo.getName() + pluginInfo.getAuthor())) { if ((temp.getName() + temp.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor())) {
Files.delete(pluginJar.toPath()); Files.delete(pluginJar.toPath());
} }
} catch (IOException e) { } catch (IOException e) {
@ -83,17 +83,17 @@ public class PluginManager {
/** /**
* 禁用插件 * 禁用插件
* *
* @param pluginInfo 需要禁用的某个插件的插件类 * @param pluginDescriptor 需要禁用的某个插件的插件类
* @apiNote * @apiNote
* @since 2023/9/11 12:34 * @since 2023/9/11 12:34
*/ */
public void disablePlugIn(PluginInfo pluginInfo) { public void disablePlugIn(PluginDescriptor pluginDescriptor) {
pluginInfo.setEnabled(false); pluginDescriptor.setEnabled(false);
pluginInfo.setPlugin(null); pluginDescriptor.setPlugin(null);
ThreadPoolManager.getThreadPool().submit(() -> { ThreadPoolManager.getThreadPool().submit(() -> {
PluginConfigController instance = PluginConfigController.getInstance(); PluginConfigController instance = PluginConfigController.getInstance();
instance.getConfig().getPlugins().forEach(plugin -> { instance.getConfig().getPlugins().forEach(plugin -> {
if ((pluginInfo.getName() + pluginInfo.getAuthor()).equals(plugin.getName() + plugin.getAuthor())) { if ((pluginDescriptor.getName() + pluginDescriptor.getAuthor()).equals(plugin.getName() + plugin.getAuthor())) {
plugin.setEnabled(false); plugin.setEnabled(false);
} }
}); });
@ -106,9 +106,9 @@ public class PluginManager {
* 初始化所有启用的插件 * 初始化所有启用的插件
*/ */
public void initializePlugins() { public void initializePlugins() {
for (PluginInfo pluginInfo : pluginInfos) { for (PluginDescriptor pluginDescriptor : pluginDescriptors) {
if (pluginInfo.isEnabled()) { if (pluginDescriptor.isEnabled()) {
pluginInfo.getPlugin().initialize(); pluginDescriptor.getPlugin().initialize();
} }
} }
} }
@ -116,12 +116,12 @@ public class PluginManager {
/** /**
* 执行插件 * 执行插件
* *
* @param pluginInfo 需要执行的插件的信息类 * @param pluginDescriptor 需要执行的插件的信息类
* @apiNote * @apiNote
* @since 2023/9/16 14:58 * @since 2023/9/16 14:58
*/ */
public void executePlugin(PluginInfo pluginInfo) { public void executePlugin(PluginDescriptor pluginDescriptor) {
pluginInfo.getPlugin().execute(); pluginDescriptor.getPlugin().execute();
} }
/** /**
@ -129,9 +129,9 @@ public class PluginManager {
* todo 待移除 * todo 待移除
*/ */
public void executePlugins() { public void executePlugins() {
for (PluginInfo pluginInfo : pluginInfos) { for (PluginDescriptor pluginDescriptor : pluginDescriptors) {
if (pluginInfo.isEnabled()) { if (pluginDescriptor.isEnabled()) {
pluginInfo.getPlugin().execute(); pluginDescriptor.getPlugin().execute();
} }
} }
} }
@ -145,7 +145,7 @@ public class PluginManager {
return categories; return categories;
} }
public List<PluginInfo> getPluginInfos() { public List<PluginDescriptor> getPluginInfos() {
return pluginInfos; return pluginDescriptors;
} }
} }

View File

@ -27,6 +27,10 @@ public class PluginManagerInterface {
private static final PluginManagerInterface INSTANCE = new PluginManagerInterface(); private static final PluginManagerInterface INSTANCE = new PluginManagerInterface();
Logger logger = LogUtil.getLogger(this.getClass()); Logger logger = LogUtil.getLogger(this.getClass());
public static PluginManagerInterface getInstance() {
return INSTANCE;
}
/** /**
* 启动插件演示界面 * 启动插件演示界面
* *
@ -54,10 +58,6 @@ public class PluginManagerInterface {
primaryStage.show(); primaryStage.show();
} }
public static PluginManagerInterface getInstance() {
return INSTANCE;
}
/** /**
* 显示已加载插件的信息 * 显示已加载插件的信息
* *

View File

@ -1,8 +1,6 @@
package org.jcnc.jnotepad.plugin.interfaces; package org.jcnc.jnotepad.plugin.interfaces;
import java.util.Map;
/** /**
* 插件接口 * 插件接口
* <p> * <p>
@ -11,6 +9,7 @@ import java.util.Map;
* @author luke gewuyou * @author luke gewuyou
*/ */
public interface Plugin { public interface Plugin {
/** /**
* 初始化插件 * 初始化插件
*/ */
@ -22,17 +21,7 @@ public interface Plugin {
void execute(); void execute();
/** /**
* 获取插件的配置参数 * 销毁资源
*
* @return 插件的配置参数
*/ */
Map<String, Object> getConfig(); void destroyed();
/**
* 设置插件的配置参数
*
* @param config 插件的配置参数
*/
void setConfig(Map<String, Object> config);
} }

View File

@ -86,112 +86,11 @@ public class AppDialogBuilder {
} }
} }
/**
* 设置应用图标
*/
public AppDialogBuilder setAppIcon(Image appIcon) {
this.appIcon = appIcon;
return this;
}
public AppDialog build() { public AppDialog build() {
appDialog = new AppDialog(this); appDialog = new AppDialog(this);
return appDialog; return appDialog;
} }
/**
* 设置对话框标题
*/
public AppDialogBuilder setTitle(String title) {
this.title = title;
return this;
}
/**
* 设置对话框头部文本
*/
public AppDialogBuilder setHeaderText(String headerText) {
this.headerText = headerText;
return this;
}
/**
* 设置自定义文本
*/
public AppDialogBuilder setCustomText(String customText) {
this.customText = customText;
return this;
}
/**
* 设置对话框宽度
*/
public AppDialogBuilder setWidth(double width) {
this.width = width;
return this;
}
/**
* 设置对话框高度
*/
public AppDialogBuilder setHeight(double height) {
this.height = height;
return this;
}
/**
* 设置对话框左侧图标
*/
public AppDialogBuilder setIcon(FontIcon icon) {
this.icon = icon;
return this;
}
/**
* 设置左按钮操作
*/
public AppDialogBuilder setLeftBtnAction(DialogButtonAction leftBtnAction) {
if (leftBtnAction != null) {
this.leftBtnAction = leftBtnAction;
}
return this;
}
/**
* 设置右按钮操作
*/
public AppDialogBuilder setRightBtnAction(DialogButtonAction rightBtnAction) {
if (rightBtnAction != null) {
this.rightBtnAction = rightBtnAction;
}
return this;
}
/**
* 设置左按钮文本
*/
public AppDialogBuilder setLeftBtnText(String leftBtnText) {
this.leftBtnText = leftBtnText;
return this;
}
/**
* 设置右按钮文本
*/
public AppDialogBuilder setRightBtnText(String rightBtnText) {
this.rightBtnText = rightBtnText;
return this;
}
/**
* 设置图标边距
*/
public AppDialogBuilder setIconCoxPaddingInsets(Insets iconCoxPaddingInsets) {
this.iconCoxPaddingInsets = iconCoxPaddingInsets;
return this;
}
/** /**
* 设置水平盒子边距 * 设置水平盒子边距
*/ */
@ -200,14 +99,6 @@ public class AppDialogBuilder {
return this; return this;
} }
/**
* 设置是否可调整大小
*/
public AppDialogBuilder setResizable(boolean resizable) {
isResizable = resizable;
return this;
}
/** /**
* 设置水平盒子间距 * 设置水平盒子间距
*/ */
@ -216,14 +107,6 @@ public class AppDialogBuilder {
return this; return this;
} }
/**
* 设置垂直盒子位置
*/
public AppDialogBuilder setVboxPos(Pos vboxPos) {
this.vboxPos = vboxPos;
return this;
}
/** /**
* 设置水平盒子位置 * 设置水平盒子位置
*/ */
@ -232,62 +115,154 @@ public class AppDialogBuilder {
return this; return this;
} }
/**
* 设置模态性
*/
public AppDialogBuilder setModality(Modality modality) {
this.modality = modality;
return this;
}
public Image getAppIcon() { public Image getAppIcon() {
return appIcon; return appIcon;
} }
/**
* 设置应用图标
*/
public AppDialogBuilder setAppIcon(Image appIcon) {
this.appIcon = appIcon;
return this;
}
public String getTitle() { public String getTitle() {
return title; return title;
} }
/**
* 设置对话框标题
*/
public AppDialogBuilder setTitle(String title) {
this.title = title;
return this;
}
public String getHeaderText() { public String getHeaderText() {
return headerText; return headerText;
} }
/**
* 设置对话框头部文本
*/
public AppDialogBuilder setHeaderText(String headerText) {
this.headerText = headerText;
return this;
}
public String getCustomText() { public String getCustomText() {
return customText; return customText;
} }
/**
* 设置自定义文本
*/
public AppDialogBuilder setCustomText(String customText) {
this.customText = customText;
return this;
}
public double getWidth() { public double getWidth() {
return width; return width;
} }
/**
* 设置对话框宽度
*/
public AppDialogBuilder setWidth(double width) {
this.width = width;
return this;
}
public double getHeight() { public double getHeight() {
return height; return height;
} }
/**
* 设置对话框高度
*/
public AppDialogBuilder setHeight(double height) {
this.height = height;
return this;
}
public FontIcon getIcon() { public FontIcon getIcon() {
return icon; return icon;
} }
/**
* 设置对话框左侧图标
*/
public AppDialogBuilder setIcon(FontIcon icon) {
this.icon = icon;
return this;
}
public DialogButtonAction getLeftBtnAction() { public DialogButtonAction getLeftBtnAction() {
return leftBtnAction; return leftBtnAction;
} }
/**
* 设置左按钮操作
*/
public AppDialogBuilder setLeftBtnAction(DialogButtonAction leftBtnAction) {
if (leftBtnAction != null) {
this.leftBtnAction = leftBtnAction;
}
return this;
}
public DialogButtonAction getRightBtnAction() { public DialogButtonAction getRightBtnAction() {
return rightBtnAction; return rightBtnAction;
} }
/**
* 设置右按钮操作
*/
public AppDialogBuilder setRightBtnAction(DialogButtonAction rightBtnAction) {
if (rightBtnAction != null) {
this.rightBtnAction = rightBtnAction;
}
return this;
}
public String getLeftBtnText() { public String getLeftBtnText() {
return leftBtnText; return leftBtnText;
} }
/**
* 设置左按钮文本
*/
public AppDialogBuilder setLeftBtnText(String leftBtnText) {
this.leftBtnText = leftBtnText;
return this;
}
public String getRightBtnText() { public String getRightBtnText() {
return rightBtnText; return rightBtnText;
} }
/**
* 设置右按钮文本
*/
public AppDialogBuilder setRightBtnText(String rightBtnText) {
this.rightBtnText = rightBtnText;
return this;
}
public Insets getIconCoxPaddingInsets() { public Insets getIconCoxPaddingInsets() {
return iconCoxPaddingInsets; return iconCoxPaddingInsets;
} }
/**
* 设置图标边距
*/
public AppDialogBuilder setIconCoxPaddingInsets(Insets iconCoxPaddingInsets) {
this.iconCoxPaddingInsets = iconCoxPaddingInsets;
return this;
}
public Insets gethBoxPaddingInsets() { public Insets gethBoxPaddingInsets() {
return hBoxPaddingInsets; return hBoxPaddingInsets;
} }
@ -296,6 +271,14 @@ public class AppDialogBuilder {
return isResizable; return isResizable;
} }
/**
* 设置是否可调整大小
*/
public AppDialogBuilder setResizable(boolean resizable) {
isResizable = resizable;
return this;
}
public double getHBoxSpacing() { public double getHBoxSpacing() {
return hBoxSpacing; return hBoxSpacing;
} }
@ -304,6 +287,14 @@ public class AppDialogBuilder {
return vboxPos; return vboxPos;
} }
/**
* 设置垂直盒子位置
*/
public AppDialogBuilder setVboxPos(Pos vboxPos) {
this.vboxPos = vboxPos;
return this;
}
public Pos getHboxPos() { public Pos getHboxPos() {
return hboxPos; return hboxPos;
} }
@ -311,4 +302,12 @@ public class AppDialogBuilder {
public Modality getModality() { public Modality getModality() {
return modality; return modality;
} }
/**
* 设置模态性
*/
public AppDialogBuilder setModality(Modality modality) {
this.modality = modality;
return this;
}
} }

View File

@ -44,9 +44,6 @@ public class UiUtil {
private static final FontIcon SUCCESS_ICON = FontIcon.of(CHECK_CIRCLE); private static final FontIcon SUCCESS_ICON = FontIcon.of(CHECK_CIRCLE);
private UiUtil() {
}
static { static {
// 暂时设置颜色 // 暂时设置颜色
ERROR_ICON.getStyleClass().addAll(Styles.DANGER); ERROR_ICON.getStyleClass().addAll(Styles.DANGER);
@ -56,6 +53,9 @@ public class UiUtil {
SUCCESS_ICON.getStyleClass().addAll(Styles.SUCCESS); SUCCESS_ICON.getStyleClass().addAll(Styles.SUCCESS);
} }
private UiUtil() {
}
/** /**
* 获取应用程序图标 * 获取应用程序图标
* *

View File

@ -5,6 +5,8 @@ import javafx.scene.layout.BorderPane;
import org.jcnc.jnotepad.exception.AppException; import org.jcnc.jnotepad.exception.AppException;
import org.jcnc.jnotepad.views.root.RootBorderPane; import org.jcnc.jnotepad.views.root.RootBorderPane;
import static org.jcnc.jnotepad.views.root.bottom.RootBottomSideBarVerticalBox.initSidebarVerticalBox;
/** /**
* 视图管理器类用于管理记事本应用程序的视图组件 * 视图管理器类用于管理记事本应用程序的视图组件
* *
@ -72,5 +74,6 @@ public class ViewManager {
root.setCenter(RootBorderPane.getInstance()); root.setCenter(RootBorderPane.getInstance());
scene.setRoot(root); scene.setRoot(root);
initSidebarVerticalBox();
} }
} }

View File

@ -2,7 +2,7 @@ package org.jcnc.jnotepad.views.root.bottom;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import org.jcnc.jnotepad.ui.module.AbstractVerticalBox; import org.jcnc.jnotepad.ui.module.AbstractVerticalBox;
import org.jcnc.jnotepad.views.root.bottom.cmd.CmdStatusBox; import org.jcnc.jnotepad.views.root.bottom.function.FunctionBox;
import org.jcnc.jnotepad.views.root.bottom.status.BottomStatusBox; import org.jcnc.jnotepad.views.root.bottom.status.BottomStatusBox;
/** /**
@ -14,7 +14,10 @@ import org.jcnc.jnotepad.views.root.bottom.status.BottomStatusBox;
*/ */
public class RootBottomSideBarVerticalBox extends AbstractVerticalBox { public class RootBottomSideBarVerticalBox extends AbstractVerticalBox {
VBox bottomSideBarVerticalBox; /**
* VBox实例
*/
private static final VBox V_BOX_INSTANCE = new VBox();
/** /**
* 获取 RootBottomSideBarVerticalBox 的唯一实例 * 获取 RootBottomSideBarVerticalBox 的唯一实例
@ -25,20 +28,30 @@ public class RootBottomSideBarVerticalBox extends AbstractVerticalBox {
return INSTANCE; return INSTANCE;
} }
public void initSidebarVerticalBox() { public RootBottomSideBarVerticalBox() {
bottomSideBarVerticalBox = new VBox();
bottomSideBarVerticalBox.getChildren().addAll(CmdStatusBox.getInstance(), BottomStatusBox.getInstance()); }
getChildren().addAll(bottomSideBarVerticalBox);
/**
* 获取vbox实例
*
* @return VBox
*/
public static VBox getVboxInstance() {
return V_BOX_INSTANCE;
} }
private static final RootBottomSideBarVerticalBox INSTANCE = new RootBottomSideBarVerticalBox(); private static final RootBottomSideBarVerticalBox INSTANCE = new RootBottomSideBarVerticalBox();
public RootBottomSideBarVerticalBox() { public static void initSidebarVerticalBox() {
initSidebarVerticalBox(); FunctionBox functionBox = FunctionBox.getInstance();
if (!FunctionBox.getMenuBar().getMenus().isEmpty()) {
functionBox.getChildren().add(FunctionBox.getMenuBar());
V_BOX_INSTANCE.getChildren().addAll(functionBox);
}
V_BOX_INSTANCE.getChildren().addAll(BottomStatusBox.getInstance());
INSTANCE.getChildren().addAll(V_BOX_INSTANCE);
} }
} }

View File

@ -1,118 +0,0 @@
package org.jcnc.jnotepad.views.root.bottom.cmd;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
import org.jcnc.jnotepad.ui.module.TerminalEmulatorComponent;
import org.jcnc.jnotepad.util.UiUtil;
/**
* CmdStatusBox 类表示应用程序的命令状态框
* <p>
* 该框包括一个菜单栏用于运行终端和构建命令用户可以点击终端菜单项以切换终端的显示状态
* 终端显示时将创建一个新的窗口以显示终端模拟器组件
*
* @author luke
*/
public class CmdStatusBox extends HBox {
Stage terminalStage = new Stage();
private static final CmdStatusBox CMD_STATUS_BOX = new CmdStatusBox();
/**
* 用于跟踪终端的显示状态
*/
private boolean terminalVisible = false;
private CmdStatusBox() {
initStatusBox();
}
/**
* 获取 CmdStatusBox 的实例
*
* @return CmdStatusBox 的实例
*/
public static CmdStatusBox getInstance() {
return CMD_STATUS_BOX;
}
/**
* 初始化命令状态框
*/
public void initStatusBox() {
var menuBar = new MenuBar();
HBox.setHgrow(menuBar, Priority.ALWAYS);
menuBar.setPadding(new Insets(-3, 0, -3, 35));
var runMenu = new Menu();
var cmdMenu = new Menu();
var buildMenu = new Menu();
var runLabel = new Label("运行");
var cmdLabel = new Label("终端");
var buildLabel = new Label("构建");
runMenu.setGraphic(runLabel);
cmdMenu.setGraphic(cmdLabel);
buildMenu.setGraphic(buildLabel);
cmdLabel.setOnMouseClicked(mouseEvent -> {
toggleTerminal(); // 切换终端的显示/隐藏状态
});
menuBar.getMenus().addAll(runMenu, cmdMenu, buildMenu);
this.getChildren().add(menuBar);
}
/**
* 切换终端的显示/隐藏状态
*/
private void toggleTerminal() {
if (terminalVisible) {
// 隐藏终端
terminalVisible = false;
hideTerminal();
} else {
// 显示终端
terminalVisible = true;
showTerminal();
}
}
/**
* 隐藏终端窗口
*/
private void hideTerminal() {
terminalStage.close();
}
/**
* 显示终端窗口
*/
private void showTerminal() {
// 创建一个新的舞台窗口
terminalStage.setTitle("终端");
terminalStage.getIcons().add(UiUtil.getAppIcon());
// 创建一个根节点布局
BorderPane root = new BorderPane();
Scene scene = new Scene(root, UiUtil.getAppWindow().getWidth() - 50, UiUtil.getAppWindow().getHeight() / 3);
// 创建TerminalEmulatorComponent并添加到根节点
TerminalEmulatorComponent terminal = new TerminalEmulatorComponent();
root.setCenter(terminal);
terminalStage.setScene(scene);
// 显示窗口
terminalStage.show();
}
}

View File

@ -0,0 +1,34 @@
package org.jcnc.jnotepad.views.root.bottom.function;
import javafx.geometry.Insets;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
/**
* 功能栏
*
* @author gewuyou
*/
public class FunctionBox extends HBox {
private static final FunctionBox INSTANCE = new FunctionBox();
private static final MenuBar MENU_BAR = new MenuBar();
static {
HBox.setHgrow(MENU_BAR, Priority.ALWAYS);
MENU_BAR.setPadding(new Insets(-3, 0, -3, 35));
}
private FunctionBox() {
}
public static FunctionBox getInstance() {
return INSTANCE;
}
public static MenuBar getMenuBar() {
return MENU_BAR;
}
}

View File

@ -0,0 +1,38 @@
package org.jcnc.jnotepad.views.root.bottom.function.interfaces;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import org.jcnc.jnotepad.views.root.bottom.function.FunctionBox;
/**
* 子功能栏抽象类(用于构建一个基本的子功能栏)
*
* @author gewuyou
*/
public abstract class AbstractFunctionChildrenBox {
protected final FunctionBox functionBox;
protected final MenuBar menuBar;
protected final Label label = new Label(getFunctionName());
protected Menu menu = new Menu();
protected AbstractFunctionChildrenBox() {
functionBox = FunctionBox.getInstance();
menuBar = FunctionBox.getMenuBar();
}
public void initialize() {
menu.setGraphic(label);
menuBar.getMenus().add(menu);
}
/**
* 获取功能按钮名称
*
* @return 功能按钮名称
*/
protected abstract String getFunctionName();
}

View File

@ -5,7 +5,6 @@
} }
/* tab修改标签样式 */ /* tab修改标签样式 */
.tab-title-editable { .tab-title-editable {
-fx-fx-border-width: 0; -fx-fx-border-width: 0;