diff --git a/pom.xml b/pom.xml index f9d8aa1bb1e7ffe1f99210c7ef6a79bce9e53251..df454d8fe00ac48eede61e7219cf7ced94c030a5 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,19 @@ system ${project.basedir}/libs/richtextfx-fat-0.11.1.jar + + + org.commonmark + commonmark + 0.21.0 + + + + org.openjfx + javafx-web + ${javafx.version} + + diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 63ed10b1d6004b8ba71dfcf3167a097c2f3fcb71..dd4e9601c30bf7c23c3acc4d6fe2273fe7f9c0c7 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -16,6 +16,11 @@ module org.jcnc.jnotepad { requires org.kordamp.ikonli.javafx; requires org.kordamp.ikonli.antdesignicons; requires richtextfx.fat; + requires java.desktop; + requires org.commonmark; + requires javafx.web; + + exports org.jcnc.jnotepad; exports org.jcnc.jnotepad.model.enums; exports org.jcnc.jnotepad.app.config; 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 2c5b3e9fe2758536b3510378ecdfed7efd64ab09..3a0566e4a603421f377fc29004f319c663206380 100644 --- a/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java +++ b/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java @@ -2,18 +2,30 @@ package org.jcnc.jnotepad.ui.pluginstage; import atlantafx.base.controls.Tile; import atlantafx.base.controls.ToggleSwitch; -import atlantafx.base.theme.PrimerLight; -import javafx.application.Application; +import atlantafx.base.theme.Styles; +import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; +import javafx.scene.web.WebEngine; +import javafx.scene.web.WebView; +import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.HtmlRenderer; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.*; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; +import javafx.scene.text.Text; import org.jcnc.jnotepad.util.LogUtil; import org.slf4j.Logger; +import java.awt.*; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; @@ -26,10 +38,24 @@ import java.util.Map; */ public class PluginManagementPane extends BorderPane { - Logger logger = LogUtil.getLogger(this.getClass()); + /** + * 图标大小常量 + */ + public static int ICON_SIZE = 40; + /** + * 日志记录器 + */ + Logger logger = LogUtil.getLogger(this.getClass()); + /** + * 自定义分割面板 + */ private CustomSplitPane customSplitPane; + + /** + * 用于存储Tile与其内容节点的映射关系 + */ private final Map tileContentMap = new HashMap<>(); /** @@ -44,7 +70,7 @@ public class PluginManagementPane extends BorderPane { */ private void init() { // 创建选项卡面板 - TabPane tabPane = new TabPane(); + TabPane rootTabPane = new TabPane(); // 创建市场、已安装和设置选项卡 Tab marketTab = new Tab("市场"); @@ -66,12 +92,11 @@ public class PluginManagementPane extends BorderPane { marketTabContent.setCenter(customSplitPane); // 获取插件列表 - var box = getBox(); - customSplitPane.setLeftContent(box); + customSplitPane.setLeftContent(getScrollPane()); // 创建示例按钮并添加到已安装和设置选项卡中 - installedTabContent.setCenter(new Button("2")); - myTabContent.setCenter(new Button("3")); + installedTabContent.setCenter(new Button("已安装")); + myTabContent.setCenter(new Button("设置")); // 将选项卡内容设置到选项卡中 marketTab.setContent(marketTabContent); @@ -79,27 +104,53 @@ public class PluginManagementPane extends BorderPane { myTab.setContent(myTabContent); // 将选项卡添加到选项卡面板中 - tabPane.getTabs().addAll(marketTab, installedTab, myTab); + rootTabPane.getTabs().addAll(marketTab, installedTab, myTab); // 将选项卡面板设置为插件管理面板的中心内容 - this.setCenter(tabPane); + this.setCenter(rootTabPane); } /** - * 创建包含插件列表的VBox。 + * 创建包含插件列表的VBox,并将其包装在滚动面板中。 * - * @return 包含插件列表的VBox + * @return 包含插件列表的滚动面板 */ - private VBox getBox() { + private ScrollPane getScrollPane() { // 创建示例插件列表项 - var tile1 = createTile("运行插件", "这是一个运行插件\t\t\t\t\t\t\t "); - var tile2 = createTile("终端插件", "这是一个终端插件"); - var tile3 = createTile("构建插件", "这是一个构建插件"); + var image1 = new Image("plug.png"); + var tile1 = createTile("运行插件", "这是一个运行插件\t\t\t\t\t\t", image1); + + var image2 = new Image("plug.png"); + var tile2 = createTile("终端插件", "这是一个终端插件", image2); + + var image3 = new Image("plug.png"); + var tile3 = createTile("构建插件", "这是一个构建插件", image3); + + var image4 = new Image("plug.png"); + var tile4 = createTile("1", "这是一个构建插件", image4); + + var image5 = new Image("plug.png"); + var tile5 = createTile("2", "这是一个构建插件", image5); + + var image6 = new Image("plug.png"); + var tile6 = createTile("4", "这是一个构建插件", image6); + + var image7 = new Image("plug.png"); + var tile7 = createTile("5", "这是一个构建插件", image7); // 创建VBox并将插件列表项添加到其中 - var box = new VBox(tile1, tile2, tile3); + var box = new VBox(tile1, tile2, tile3, tile4, tile5, tile6, tile7); + + // 创建滚动面板并将VBox设置为其内容 + var scrollPane = new ScrollPane(box); + + // 设置滚动面板的宽度适应父容器 + scrollPane.setFitToWidth(true); + // 隐藏滚动条 + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); - return box; + return scrollPane; } /** @@ -107,24 +158,37 @@ public class PluginManagementPane extends BorderPane { * * @param title 插件标题 * @param description 插件描述 + * @param image 插件图标 * @return 创建的插件列表项Tile */ - private Tile createTile(String title, String description) { + private Tile createTile(String title, String description, Image image) { + // 创建一个title var tile = new Tile(title, description); + // 创建一个按钮 var tgl = new ToggleSwitch(); + // 创建一个图标 + ImageView icon = new ImageView(image); + // 指定要缩放的固定像素大小 + double iconSize = ICON_SIZE; + + // 设置图像视图的宽度和高度,以便等比例缩放到指定像素大小 + icon.setFitWidth(iconSize); + icon.setFitHeight(iconSize); + + // 设置Tile的图标 + tile.setGraphic(icon); + // 设置Tile的操作和操作处理程序 tile.setAction(tgl); tile.setActionHandler(() -> { customSplitPane.setRightContent(tileContentMap.get(tile)); logger.info("点击了" + tile); - }); // 创建专属的customSplitPane内容 var content = createCustomSplitPaneContent(title); - // 将内容与Tile关联起来 tileContentMap.put(tile, content); @@ -134,12 +198,142 @@ public class PluginManagementPane extends BorderPane { /** * 创建专属于每个插件的CustomSplitPane内容。 * - * @param title 插件标题 + * @param titleName 插件标题 * @return 创建的CustomSplitPane内容 */ - private Node createCustomSplitPaneContent(String title) { - // TODO: 2023/9/23 未完成 - return new Label("详情" + title); + private Node createCustomSplitPaneContent(String titleName) { + VBox content = new VBox(8); + content.setPadding(new Insets(10)); + var titleLabel = new Text(titleName); + titleLabel.getStyleClass().addAll(Styles.TITLE_1); + + var authorBox = new HBox(10); + var author = new Text("JCNC团队"); + var authorLink = getAuthorLink(); + authorBox.getChildren().addAll(author, authorLink); + + var state = new Text("未启用"); + + var main = new VBox(10); + + // 创建TabPane并添加标签页 + TabPane tabPane = new TabPane(); + + Tab detailsTab = new Tab("细节"); + detailsTab.setClosable(false); + Tab featuresTab = new Tab("实现功能"); + featuresTab.setClosable(false); + Tab changelogTab = new Tab("更新日志"); + changelogTab.setClosable(false); + + // 在标签页中添加内容 + VBox detailsContent = new VBox(10); + + // 创建一个WebView来显示Markdown内容 + WebView webView = new WebView(); + WebEngine engine = webView.getEngine(); + + // 从外部文件加载Markdown内容 + String markdownContent = loadMarkdownFromFile("README.md"); + String htmlContent = markdownToHtml(markdownContent); + + // 加载HTML内容到WebView + engine.loadContent(htmlContent); + // 将WebView添加到detailsContent + detailsContent.getChildren().addAll(webView); + + VBox featuresContent = new VBox(10); + VBox changelogContent = new VBox(10); + + detailsTab.setContent(detailsContent); + featuresTab.setContent(featuresContent); + changelogTab.setContent(changelogContent); + + tabPane.getTabs().addAll(detailsTab, featuresTab, changelogTab); + + main.getChildren().addAll(tabPane); + + content.getChildren().addAll(titleLabel, authorBox, state, main); + + // 将内容包装在滚动面板中 + ScrollPane scrollPane = new ScrollPane(content); + scrollPane.setFitToWidth(true); + scrollPane.setFitToHeight(true); + + return scrollPane; + } + + /** + * 将Markdown内容转换为HTML格式。 + * + * @param markdownContent Markdown格式的内容 + * @return HTML格式的内容 + */ + private String markdownToHtml(String markdownContent) { + // 创建Markdown解析器 + Parser parser = Parser.builder().build(); + + // 解析Markdown内容 + + org.commonmark.node.Node document = parser.parse(markdownContent); + + // 创建HTML渲染器 + HtmlRenderer renderer = HtmlRenderer.builder().build(); + + // 将Markdown文档渲染为HTML + return renderer.render(document); + } + + /** + * 从文件加载Markdown内容。 + * + * @param filePath 文件路径 + * @return 加载的Markdown内容 + */ + private String loadMarkdownFromFile(String filePath) { + try { + return new String(Files.readAllBytes(Paths.get(filePath))); + } catch (IOException e) { + logger.info("正在运行" + "loadMarkdownFromFile"); + return ""; + } + } + + /** + * 获取作者链接。 + * + * @return 作者链接 + */ + private Hyperlink getAuthorLink() { + var authorLink = new Hyperlink("插件仓库地址"); + authorLink.setVisited(true); + authorLink.setStyle("-fx-text-fill: blue; -fx-visited-link-color: blue;"); + authorLink.setOnAction(event -> { + // 定义要打开的链接 + String url = "https://gitee.com/jcnc-org/JNotepad"; + + try { + // 创建URI对象 + URI uri = new URI(url); + // 检查系统是否支持Desktop类 + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + + // 检查是否支持浏览器启动 + if (desktop.isSupported(Desktop.Action.BROWSE)) { + // 打开默认浏览器并访问链接 + desktop.browse(uri); + } else { + logger.info("系统不支持浏览器启动操作!"); + } + } else { + logger.info("系统不支持Desktop类!"); + } + } catch (Exception e) { + logger.info("启动" + authorLink + "失败!"); + } + }); + return authorLink; } } diff --git a/src/main/java/org/jcnc/jnotepad/views/root/top/menu/TopMenuBar.java b/src/main/java/org/jcnc/jnotepad/views/root/top/menu/TopMenuBar.java index 18e1bb5116a3e7ab1494af9f11e2c71f8a08cea5..4798632fbf57ac809d08e7410feef66772f3343e 100644 --- a/src/main/java/org/jcnc/jnotepad/views/root/top/menu/TopMenuBar.java +++ b/src/main/java/org/jcnc/jnotepad/views/root/top/menu/TopMenuBar.java @@ -277,7 +277,7 @@ public class TopMenuBar extends MenuBar { PluginManagementPane pluginManagementPane = new PluginManagementPane(); - Scene scene = new Scene(pluginManagementPane, 900, 500); + Scene scene = new Scene(pluginManagementPane, 900, 600); newStage.setScene(scene); newStage.show(); }); diff --git a/src/main/resources/plug.png b/src/main/resources/plug.png new file mode 100644 index 0000000000000000000000000000000000000000..68b925df731c9c302471b890cb6e6fbc03f6f1fe Binary files /dev/null and b/src/main/resources/plug.png differ