diff --git a/pom.xml b/pom.xml index 9b6996a15883ae3f41b88f22161920691daeccd0..b5d54d5cb6daaf26b00db3f0caf2f12fc303f705 100644 --- a/pom.xml +++ b/pom.xml @@ -84,6 +84,11 @@ system ${project.basedir}/libs/icu4j-73.2.jar + + org.fxmisc.richtext + richtextfx + 0.11.1 + diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 392cd89f6fdde7b3b0a537ccf9bc01658400bed6..d022a262f7a95ef528feee9eb143592f1384405a 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -16,6 +16,7 @@ module org.jcnc.jnotepad { requires org.kordamp.ikonli.javafx; requires org.kordamp.ikonli.antdesignicons; requires org.fxmisc.richtext; + requires org.fxmisc.flowless; exports org.jcnc.jnotepad; exports org.jcnc.jnotepad.model.enums; exports org.jcnc.jnotepad.app.config; @@ -30,7 +31,6 @@ module org.jcnc.jnotepad { exports org.jcnc.jnotepad.common.interfaces; opens org.jcnc.jnotepad.app.config; exports org.jcnc.jnotepad.views.root.center.main.bottom.status; - exports org.jcnc.jnotepad.plugin.interfaces; exports org.jcnc.jnotepad.ui.dialog; exports org.jcnc.jnotepad.ui.dialog.interfaces; exports org.jcnc.jnotepad.model.entity; diff --git a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java index a9c1fe1e7ecdc3537d400d819ffac38c732cdfbc..7463e8ba8f1890b81069693945723e316e92d802 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java +++ b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java @@ -6,6 +6,7 @@ import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.control.Tab; import javafx.stage.FileChooser; +import org.fxmisc.flowless.VirtualizedScrollPane; import org.jcnc.jnotepad.app.i18n.UiResourceBundle; import org.jcnc.jnotepad.common.constants.TextConstants; import org.jcnc.jnotepad.common.manager.ThreadPoolManager; diff --git a/src/main/java/org/jcnc/jnotepad/model/entity/PluginInfo.java b/src/main/java/org/jcnc/jnotepad/model/entity/PluginInfo.java index 42f1b6b922ff07b15b3fae2af1de164c5fdc7087..4616f56cd6366594899e74ec8e35aa403655e287 100644 --- a/src/main/java/org/jcnc/jnotepad/model/entity/PluginInfo.java +++ b/src/main/java/org/jcnc/jnotepad/model/entity/PluginInfo.java @@ -9,7 +9,7 @@ public class PluginInfo { /** * 插件名称 */ - private String name; + private String pluginName; /** * 插件版本 */ @@ -19,17 +19,12 @@ public class PluginInfo { */ private boolean enabled; - /** - * 主类名称 - */ - private String mainClass; - - public String getName() { - return name; + public String getPluginName() { + return pluginName; } - public void setName(String name) { - this.name = name; + public void setPluginName(String pluginName) { + this.pluginName = pluginName; } public String getVersion() { @@ -47,12 +42,4 @@ public class PluginInfo { public void setEnabled(boolean enabled) { this.enabled = enabled; } - - public String getMainClass() { - return mainClass; - } - - public void setMainClass(String mainClass) { - this.mainClass = mainClass; - } } diff --git a/src/main/java/org/jcnc/jnotepad/plugin/ButtonPlugin.java b/src/main/java/org/jcnc/jnotepad/plugin/ButtonPlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..81d6ac3feb60e0a5c1a1d3d2a5d76e237eef0abd --- /dev/null +++ b/src/main/java/org/jcnc/jnotepad/plugin/ButtonPlugin.java @@ -0,0 +1,58 @@ +package org.jcnc.jnotepad.plugin; + +import org.jcnc.jnotepad.plugin.interfaces.Plugin; +import org.jcnc.jnotepad.util.LogUtil; + +import java.util.Map; + +/** + * 新按钮插件 + * + * @author luke + */ +public class ButtonPlugin implements Plugin { + + @Override + public String getCategoryName() { + return "新按钮插件"; + } + + @Override + public String getDisplayName() { + return "新按钮"; + } + + /** + * 初始化插件 + */ + @Override + public void initialize() { + LogUtil.getLogger(this.getClass()).info("新按钮插件初始化了!"); + } + + @Override + public void execute() { + // 在这里实现新按钮插件的逻辑 + LogUtil.getLogger(this.getClass()).info("新按钮插件执行了!"); + } + + /** + * 获取插件的配置参数 + * + * @return 插件的配置参数 + */ + @Override + public Map getConfig() { + return null; + } + + /** + * 设置插件的配置参数 + * + * @param config 插件的配置参数 + */ + @Override + public void setConfig(Map config) { + + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/jnotepad/plugin/PluginDemo.java b/src/main/java/org/jcnc/jnotepad/plugin/PluginDemo.java index 8ce4d9859d0d8d8ec7b8376262eb68dc244b02eb..af42ec768db0725e83fe7a1c623462cd03e18c20 100644 --- a/src/main/java/org/jcnc/jnotepad/plugin/PluginDemo.java +++ b/src/main/java/org/jcnc/jnotepad/plugin/PluginDemo.java @@ -8,9 +8,7 @@ import javafx.stage.FileChooser; import javafx.stage.Stage; import org.jcnc.jnotepad.ui.dialog.factory.impl.BasicFileChooserFactory; import org.jcnc.jnotepad.util.LogUtil; -import org.jcnc.jnotepad.util.PopUpUtil; import org.jcnc.jnotepad.util.UiUtil; -import org.slf4j.Logger; import java.io.File; import java.util.List; @@ -24,7 +22,6 @@ import java.util.Map; * @author luke */ public class PluginDemo { - Logger logger = LogUtil.getLogger(this.getClass()); /** * 启动插件演示界面 @@ -56,8 +53,8 @@ public class PluginDemo { /** * 创建加载插件的按钮 * - * @param primaryStage JavaFX的主舞台 - * @param fileChooser 文件选择器 + * @param primaryStage JavaFX的主舞台 + * @param fileChooser 文件选择器 * @param pluginManager 插件管理器 * @return 加载插件的按钮 */ @@ -68,16 +65,13 @@ public class PluginDemo { File selectedFile = fileChooser.showOpenDialog(primaryStage); if (selectedFile != null) { String pluginFilePath = selectedFile.getAbsolutePath(); - PluginLoader.getInstance().loadPlugins(pluginFilePath); + pluginManager.loadPlugins(pluginFilePath); // 更新插件信息显示 displayPluginInfo(primaryStage, pluginManager); - } else { - PopUpUtil.infoAlert(null, null, "未找到插件!", null, null); - logger.info("未找到插件!"); } - } catch (Exception e) { - logger.error("加载插件失败!", e); + } catch (Exception ignored) { + LogUtil.getLogger(this.getClass()).info("未加载插件!"); } }); return loadButton; diff --git a/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java b/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java deleted file mode 100644 index 9bf5f37c405ff5ee74e5f1c97773e170d908b279..0000000000000000000000000000000000000000 --- a/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.jcnc.jnotepad.plugin; - -import org.jcnc.jnotepad.exception.AppException; -import org.jcnc.jnotepad.model.entity.PluginInfo; -import org.jcnc.jnotepad.plugin.interfaces.Plugin; -import org.jcnc.jnotepad.util.JsonUtil; -import org.jcnc.jnotepad.util.LogUtil; -import org.slf4j.Logger; - -import java.io.*; -import java.lang.reflect.InvocationTargetException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.jar.JarFile; -import java.util.zip.ZipEntry; - -/** - * 插件加载类 - * - * @author gewuyou - */ -public class PluginLoader { - private static final PluginLoader INSTANCE = new PluginLoader(); - Logger logger = LogUtil.getLogger(this.getClass()); - - /** - * 从插件jar包中读取 json 文件到 PluginInfo 类 - * - * @param pluginJar jar 包 - */ - private static PluginInfo readPlugin(File pluginJar) throws IOException { - InputStream is; - StringBuilder sb; - try (JarFile jarFile = new JarFile(pluginJar)) { - ZipEntry zipEntry = jarFile.getEntry("META-INF/plugin.json"); - is = jarFile.getInputStream(zipEntry); - - try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) { - String temp; - sb = new StringBuilder(); - while ((temp = br.readLine()) != null) { - sb.append(temp); - } - } - } - return JsonUtil.OBJECT_MAPPER.readValue(sb.toString(), PluginInfo.class); - } - - public static PluginLoader getInstance() { - return INSTANCE; - } - - /** - * 加载插件 - * - * @param pluginFilePath 插件文件的路径 - */ - public void loadPlugins(String pluginFilePath) { - PluginManager pluginManager = PluginManager.getInstance(); - List plugins = pluginManager.getPlugins(); - Map> categories = pluginManager.getLoadedPluginsByCategory(); - Map pluginInfos = pluginManager.getPluginInfos(); - File file = new File(pluginFilePath); - if (file.exists() && file.isFile()) { - try { - PluginInfo pluginInfo = readPlugin(file); - pluginInfos.put(pluginInfo.getName(), pluginInfo); - // 创建URLClassLoader以加载Jar文件中的类 - Class pluginClass; - try (URLClassLoader classLoader = new URLClassLoader(new URL[]{file.toURI().toURL()})) { - pluginClass = classLoader.loadClass(pluginInfo.getMainClass()); - } - if (pluginClass == null) { - return; - } - Plugin plugin; - plugin = (Plugin) pluginClass.getDeclaredConstructor().newInstance(); - plugins.add(plugin); - // 添加插件到类别中 - String categoryName = plugin.getCategoryName(); - String displayName = plugin.getDisplayName(); - categories.computeIfAbsent(categoryName, k -> new ArrayList<>()).add(displayName); - } catch (IOException | InvocationTargetException | InstantiationException | IllegalAccessException e) { - throw new AppException(e); - } catch (ClassNotFoundException e) { - logger.error("无法找到对应的插件类!", e); - } catch (NoSuchMethodException e) { - logger.error("插件类中没有找到指定方法!", e); - } - - } else { - LogUtil.getLogger(this.getClass()).info("PluginInfo file not found: {}", pluginFilePath); - } - } -} diff --git a/src/main/java/org/jcnc/jnotepad/plugin/PluginManager.java b/src/main/java/org/jcnc/jnotepad/plugin/PluginManager.java index 380276a845a6a591d042488d3dea2ec540a5ce21..4cfef568ef79ee1ed55c2a73c7fb4d505158f33e 100644 --- a/src/main/java/org/jcnc/jnotepad/plugin/PluginManager.java +++ b/src/main/java/org/jcnc/jnotepad/plugin/PluginManager.java @@ -1,10 +1,14 @@ package org.jcnc.jnotepad.plugin; -import org.jcnc.jnotepad.model.entity.PluginInfo; import org.jcnc.jnotepad.plugin.interfaces.Plugin; import org.jcnc.jnotepad.util.LogUtil; import org.slf4j.Logger; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -21,19 +25,8 @@ import java.util.Map; public class PluginManager { private static final PluginManager INSTANCE = new PluginManager(); Logger logger = LogUtil.getLogger(this.getClass()); - /** - * 插件集合 - */ private final List plugins = new ArrayList<>(); - /** - * 插件类别 - */ private final Map> categories = new HashMap<>(); - /** - * 插件信息 - */ - - private final Map pluginInfos = new HashMap<>(); private PluginManager() { @@ -43,6 +36,47 @@ public class PluginManager { return INSTANCE; } + /** + * 加载插件 + * + * @param pluginFilePath 插件文件的路径 + */ + public void loadPlugins(String pluginFilePath) { + File file = new File(pluginFilePath); + if (file.exists() && file.isFile()) { + // 创建URLClassLoader以加载Jar文件中的类 + Class pluginClass = null; + try (URLClassLoader classLoader = new URLClassLoader(new URL[]{file.toURI().toURL()})) { + pluginClass = classLoader.loadClass("org.jcnc.jnotepad.plugin.ButtonPlugin"); + } catch (ClassNotFoundException e) { + logger.error("无法找到对应的插件类!", e); + } catch (MalformedURLException e) { + logger.error("无法创建URL格式错误!", e); + } catch (IOException e) { + logger.error("IO异常!", e); + } + if (pluginClass == null) { + return; + } + Plugin plugin = null; + try { + plugin = (Plugin) pluginClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + logger.error("发生异常!", e); + } + if (plugin == null) { + return; + } + plugins.add(plugin); + + // 添加插件到类别中 + String categoryName = plugin.getCategoryName(); + String displayName = plugin.getDisplayName(); + categories.computeIfAbsent(categoryName, k -> new ArrayList<>()).add(displayName); + } else { + LogUtil.getLogger(this.getClass()).info("PluginInfo file not found: {}", pluginFilePath); + } + } /** * 卸载插件 @@ -82,12 +116,4 @@ public class PluginManager { public Map> getLoadedPluginsByCategory() { return categories; } - - public List getPlugins() { - return plugins; - } - - public Map getPluginInfos() { - return pluginInfos; - } } diff --git a/src/main/java/org/jcnc/jnotepad/ui/module/LineNumberTextArea.java b/src/main/java/org/jcnc/jnotepad/ui/module/LineNumberTextArea.java index 27c969eb61dbc21a73e622b7e9b6d8d3c4577fc9..27a9fb06f33fcabde405be564ef760e03410584d 100644 --- a/src/main/java/org/jcnc/jnotepad/ui/module/LineNumberTextArea.java +++ b/src/main/java/org/jcnc/jnotepad/ui/module/LineNumberTextArea.java @@ -2,6 +2,7 @@ package org.jcnc.jnotepad.ui.module; import org.fxmisc.richtext.CodeArea; import org.fxmisc.richtext.LineNumberFactory; +import org.fxmisc.richtext.StyleClassedTextArea; import org.jcnc.jnotepad.util.LogUtil; import org.jcnc.jnotepad.views.root.center.main.bottom.status.BottomStatusBox; import org.jcnc.jnotepad.views.root.center.main.center.tab.CenterTab; @@ -20,7 +21,7 @@ import java.io.IOException; * * @author luke */ -public class LineNumberTextArea extends CodeArea { +public class LineNumberTextArea extends StyleClassedTextArea { /** @@ -28,20 +29,16 @@ public class LineNumberTextArea extends CodeArea { */ private static final Logger logger = LogUtil.getLogger(LineNumberTextArea.class); + /** * 构造函数 *

* 用于创建 LineNumberTextArea 对象 */ public LineNumberTextArea() { - // 设置 LineNumberTextArea 的样式,包括边框和背景颜色 - this.setStyle( - "-fx-border-color:white;" + - "-fx-background-color:white" - ); + getStyleClass().add("line-number-text-area"); this.setParagraphGraphicFactory(LineNumberFactory.get(this)); initListeners(); - } /** @@ -55,6 +52,7 @@ public class LineNumberTextArea extends CodeArea { }); } + /** * 保存方法 */ @@ -79,6 +77,7 @@ public class LineNumberTextArea extends CodeArea { return; } + // 尝试使用BufferedWriter写入文件内容 try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, tab.getCharset()))) { // 将新的文本内容写入文件 @@ -90,4 +89,5 @@ public class LineNumberTextArea extends CodeArea { LogUtil.getLogger(this.getClass()).info("已忽视IO异常!"); } } + } \ No newline at end of file diff --git a/src/main/java/org/jcnc/jnotepad/views/root/center/main/bottom/status/BottomStatusBox.java b/src/main/java/org/jcnc/jnotepad/views/root/center/main/bottom/status/BottomStatusBox.java index 4a064dcddb6249a2b37cf41d1756e23b095af813..e00b3ff0115d8aad7bb1b6c1b4ba96145fde2982 100644 --- a/src/main/java/org/jcnc/jnotepad/views/root/center/main/bottom/status/BottomStatusBox.java +++ b/src/main/java/org/jcnc/jnotepad/views/root/center/main/bottom/status/BottomStatusBox.java @@ -3,6 +3,7 @@ package org.jcnc.jnotepad.views.root.center.main.bottom.status; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; +import javafx.scene.control.TextArea; import org.jcnc.jnotepad.app.i18n.UiResourceBundle; import org.jcnc.jnotepad.common.constants.TextConstants; import org.jcnc.jnotepad.ui.module.AbstractHorizontalBox; diff --git a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java index db93f424eff433b3e64eada148563e562c797f69..634f31e0bd667f530617b430e0602b83dd9a27d5 100644 --- a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java +++ b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java @@ -1,6 +1,7 @@ package org.jcnc.jnotepad.views.root.center.main.center.tab; import javafx.scene.control.Tab; +import org.fxmisc.flowless.VirtualizedScrollPane; import org.jcnc.jnotepad.controller.config.AppConfigController; import org.jcnc.jnotepad.ui.module.LineNumberTextArea; @@ -37,7 +38,7 @@ public class CenterTab extends Tab { public CenterTab(String tabTitle, LineNumberTextArea textArea, Charset charset) { super(tabTitle); lineNumberTextArea = textArea; - this.setContent(lineNumberTextArea); + this.setContent(new VirtualizedScrollPane<>(lineNumberTextArea)); setAutoLine(AppConfigController.getInstance().getAutoLineConfig()); this.charset = charset; } @@ -56,7 +57,7 @@ public class CenterTab extends Tab { public void setAutoLine(boolean autoLine) { this.autoLine = autoLine; - lineNumberTextArea.setWrapText(autoLine); +// lineNumberTextArea.getMainTextArea().setWrapText(autoLine); } public LineNumberTextArea getLineNumberTextArea() { diff --git a/src/main/java/org/jcnc/jnotepad/views/root/left/sidebar/tools/ToolHorizontalBox.java b/src/main/java/org/jcnc/jnotepad/views/root/left/sidebar/tools/ToolHorizontalBox.java index 0292664143762ff01ed2b88cbf10fb7e14da2025..bb5353b32d7f0fda03d028452b149ba0b6173615 100644 --- a/src/main/java/org/jcnc/jnotepad/views/root/left/sidebar/tools/ToolHorizontalBox.java +++ b/src/main/java/org/jcnc/jnotepad/views/root/left/sidebar/tools/ToolHorizontalBox.java @@ -25,6 +25,7 @@ public class ToolHorizontalBox extends AbstractHorizontalBox { // 将 JNotepadToolBar 添加为子节点 getChildren().add(SidebarToolBar.getInstance()); + getStyleClass().add("tool-horizontal-box"); } /** diff --git a/src/main/resources/css/styles.css b/src/main/resources/css/styles.css index 1cdab11e577164606a4380e9877766f36eac53f4..05a3f3e2e3cadd80415ede790eb4194cf2b7d121 100644 --- a/src/main/resources/css/styles.css +++ b/src/main/resources/css/styles.css @@ -4,29 +4,7 @@ -fx-vbar-policy: as-needed; } -/*!* 不显示滚动条 *!*/ -/*.text-line-number .content {*/ -/* -fx-cursor: text;*/ -/* -fx-padding: 8px 1px 8px 5px;*/ -/*}*/ -.text-line-number .content { - -fx-padding: 8px 1px 8px 5px; -} -.text-line-number .scroll-bar:vertical { - -fx-pref-width: 1; - -fx-opacity: 0; -} -.text-line-number .scroll-bar:horizontal { - -fx-pref-height: 10; - -fx-opacity: 0; -} - -/* 主文本框区域样式 */ -.main-text-area { - -fx-border-color: white; - -fx-background-color: white; -} /* tab修改标签样式 */ .tab-title-editable { @@ -35,4 +13,25 @@ -fx-max-height: 20px; -fx-font-size: 14px; -fx-padding: 2px 2px 2px 2px; +} + +/*左侧边栏*/ +.tool-horizontal-box { + -fx-border-width: 0 1 0 0; + -fx-border-color: -color-border-default; +} + + +.paragraph-box:has-caret { + -fx-background-color: -color-neutral-muted +} + +.line-number-text-area .paragraph-box .text { + /*-fx-fill: -color-fg-default;*/ + /* -fx-font-size: 18px;*/ +} + +.lineno { + -fx-background-color: -color-bg-default; + /*-fx-padding: 0 10 0 0;*/ } \ No newline at end of file