feat(box): 现代化用户界面并添加 jar 文件预览功能

- 使用 FlatDarculaLaf 样式库替换默认样式
- 添加 jar 文件预览功能,使用 CFR 进行反编译
- 更新 build.gradle 文件,添加新依赖项
- 新增 CFROutputSinkFactory 和 JarClassFileSource 类
- 修改主程序启动逻辑,支持 jar 文件预览
This commit is contained in:
tzdwindows 7
2025-02-23 13:31:14 +08:00
parent ce996b73be
commit e422d31b85
12 changed files with 1225 additions and 11 deletions

View File

@@ -2,6 +2,7 @@ plugins {
id 'java' id 'java'
id 'application' id 'application'
id 'edu.sc.seis.launch4j' version '2.5.4' id 'edu.sc.seis.launch4j' version '2.5.4'
id 'org.openjfx.javafxplugin' version '0.1.0'
} }
// JDK 版本检查 // JDK 版本检查
@@ -58,7 +59,23 @@ dependencies {
implementation 'org.python:jython-standalone:2.7.3' implementation 'org.python:jython-standalone:2.7.3'
implementation 'org.fxmisc.richtext:richtextfx:0.11.0' // 更新后的richtextfx
implementation 'org.bitbucket.mstrobel:procyon-core:0.5.36' // 使用JitPack版本
implementation 'org.bitbucket.mstrobel:procyon-compilertools:0.5.36'
implementation 'com.fifesoft:rsyntaxtextarea:3.3.0'
implementation 'org.apache.commons:commons-compress:1.23.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
implementation 'org.controlsfx:controlsfx:11.1.2' // 现代化UI组件
implementation 'com.dlsc.formsfx:formsfx-core:11.6.0' // 表单组件
implementation 'net.sourceforge.plantuml:plantuml:8059' // UML支持可选)
implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.google.code.gson:gson:2.10.1'
implementation 'org.openjfx:javafx-controls:21'
implementation 'org.benf:cfr:0.152'
implementation 'com.github.javaparser:javaparser-core:3.25.1'
} }
// 分离依赖项到 libs 目录 // 分离依赖项到 libs 目录
@@ -108,6 +125,11 @@ tasks.withType(JavaExec).configureEach {
] ]
} }
javafx {
version = "21"
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
// 单独打包文档 // 单独打包文档
task packageOpenSourceDocs(type: Jar) { task packageOpenSourceDocs(type: Jar) {
archiveClassifier = 'docs' archiveClassifier = 'docs'
@@ -122,7 +144,8 @@ application {
// 确保运行时参数生效 // 确保运行时参数生效
applicationDefaultJvmArgs = [ applicationDefaultJvmArgs = [
"-Djava.system.class.loader=com.axis.innovators.box.plugins.BoxClassLoader", "-Djava.system.class.loader=com.axis.innovators.box.plugins.BoxClassLoader",
"-Dloader.library.path=$buildDir/libs/libs" "-Dloader.library.path=$buildDir/libs/libs",
'-Dfile.encoding=UTF-8'
] ]
} }

Binary file not shown.

View File

@@ -5,13 +5,41 @@
版本1.1 版本1.1
""" """
from com.axis.innovators.box.python import PyLocalSide from com.axis.innovators.box.python import PyLocalSide
from javax.swing import AbstractAction
from javax.swing import AbstractAction, JFrame,JDialog, JLabel, JButton, JPanel, JOptionPane
from java.awt import BorderLayout, Dimension
from java.awt.event import ActionListener
class MyAction(AbstractAction): class MyAction(AbstractAction):
def actionPerformed(self, event): def actionPerformed(self, event):
"""工具项点击事件处理""" """工具项点击事件处理"""
print("[DEBUG] Tool item clicked! Event source:", event.getSource()) print("[DEBUG] Tool item clicked! Event source:", event.getSource())
# 创建 JDialog 作为子窗口
parent_frame = event.getSource().getTopLevelAncestor() # 获取父窗口
dialog = JDialog(parent_frame, u"工具面板", True) # True 表示模态对话框
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE) # 关闭时释放资源
dialog.setSize(300, 200)
dialog.setLocationRelativeTo(parent_frame) # 居中于父窗口
# 创建面板并设置布局
panel = JPanel()
panel.layout = BorderLayout() # 使用边界布局
# 添加标签
label = JLabel(u"这是工具项的子面板")
panel.add(label, BorderLayout.CENTER)
# 添加关闭按钮
close_btn = JButton(u"关闭")
close_btn.addActionListener(lambda e: dialog.dispose()) # 点击关闭窗口
panel.add(close_btn, BorderLayout.SOUTH)
# 将面板添加到窗口
dialog.add(panel)
dialog.visible = True # 显示窗口
def onStartup(): def onStartup():
""" """
系统启动时自动执行的初始化逻辑 系统启动时自动执行的初始化逻辑

View File

@@ -1,5 +1,6 @@
package com.axis.innovators.box; package com.axis.innovators.box;
import com.axis.innovators.box.decompilation.gui.ModernJarViewer;
import com.axis.innovators.box.events.GlobalEventBus; import com.axis.innovators.box.events.GlobalEventBus;
import com.axis.innovators.box.events.OpenFileEvents; import com.axis.innovators.box.events.OpenFileEvents;
import com.axis.innovators.box.events.StartupEvent; import com.axis.innovators.box.events.StartupEvent;
@@ -26,6 +27,7 @@ import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.io.*; import java.io.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -334,11 +336,24 @@ public class AxisInnovatorsBox {
try { try {
main.initLog4j2(); main.initLog4j2();
main.setTopic(); main.setTopic();
List<Map<String, String>> validFiles = ArgsParser.parseArgs(args); List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
for (Map<String, String> fileInfo : validFiles) { for (Map<String, String> fileInfo : validFiles) {
OpenFileEvents openFileEvents = new OpenFileEvents(fileInfo.get("path"), fileInfo.get("extension")); String extension = fileInfo.get("extension");
String path = fileInfo.get("path");
if (".jar".equals(extension)){
SwingUtilities.invokeLater(() -> {
try {
UIManager.setLookAndFeel(new com.formdev.flatlaf.FlatDarculaLaf());
} catch (Exception ex) {
ex.printStackTrace();
}
ModernJarViewer viewer = new ModernJarViewer(null, path);
main.popupWindow(viewer);
});
main.progressBarManager.close();
return;
}
OpenFileEvents openFileEvents = new OpenFileEvents(path, extension);
GlobalEventBus.EVENT_BUS.post(openFileEvents); GlobalEventBus.EVENT_BUS.post(openFileEvents);
if (!openFileEvents.isContinue()) { if (!openFileEvents.isContinue()) {
return; return;

View File

@@ -4,6 +4,8 @@ import com.axis.innovators.box.tools.FolderCleaner;
import com.axis.innovators.box.tools.FolderCreator; import com.axis.innovators.box.tools.FolderCreator;
import com.axis.innovators.box.register.LanguageManager; import com.axis.innovators.box.register.LanguageManager;
import java.util.Arrays;
/** /**
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
@@ -16,6 +18,7 @@ public class Main {
LanguageManager.loadLanguage("system:zh_CN"); LanguageManager.loadLanguage("system:zh_CN");
} }
System.out.println(Arrays.toString(args));
AxisInnovatorsBox.run(args); AxisInnovatorsBox.run(args);
} }
} }

View File

@@ -0,0 +1,51 @@
package com.axis.innovators.box.decompilation.gui;
import org.benf.cfr.reader.api.OutputSinkFactory;
import org.benf.cfr.reader.api.SinkReturns.Decompiled;
import org.benf.cfr.reader.api.SinkReturns.ExceptionMessage;
import org.benf.cfr.reader.api.SinkReturns;
import javax.swing.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class CFROutputSinkFactory implements OutputSinkFactory {
private JTextArea codeArea;
private StringBuilder output = new StringBuilder();
public CFROutputSinkFactory(JTextArea codeArea) {
this.codeArea = codeArea;
}
public CFROutputSinkFactory() {
}
@Override
public List<SinkClass> getSupportedSinks(SinkType sgetOutputinkType, Collection<SinkClass> collection) {
return Collections.singletonList(SinkClass.DECOMPILED);
}
public String getOutput() {
return output.toString();
}
@Override
public <T> Sink<T> getSink(SinkType sinkType, SinkClass sinkClass) {
if (sinkClass == SinkClass.DECOMPILED) {
return (Sink<T>) new Sink<SinkReturns.Decompiled>() {
@Override
public void write(Decompiled decompiled) {
if (codeArea != null) {
SwingUtilities.invokeLater(() ->
codeArea.setText(decompiled.getJava())
);
}
output.append(decompiled.getJava()).append("\n");
}
};
}
return null;
}
}

View File

@@ -0,0 +1,47 @@
package com.axis.innovators.box.decompilation.gui;
import org.benf.cfr.reader.api.ClassFileSource;
import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class JarClassFileSource implements ClassFileSource {
private final JarFile jarFile;
public JarClassFileSource(JarFile jarFile) {
this.jarFile = jarFile;
}
@Override
public void informAnalysisRelativePathDetail(String usePath, String classFilePath) {
}
@Override
public Collection<String> addJar(String jarPath) {
return Collections.emptyList();
}
@Override
public String getPossiblyRenamedPath(String path) {
return path;
}
@Override
public Pair<byte[], String> getClassFileContent(String path) throws IOException {
JarEntry entry = jarFile.getJarEntry(path);
if (entry == null) {
return null;
}
try (InputStream is = jarFile.getInputStream(entry)) {
byte[] bytes = new byte[(int) entry.getSize()];
is.read(bytes);
return Pair.make(bytes, path);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,7 @@ public class LoadIcon {
* @param size 图片大小 * @param size 图片大小
* @return ImageIcon对象 * @return ImageIcon对象
*/ */
static ImageIcon loadIcon(String filename, int size) { public static ImageIcon loadIcon(String filename, int size) {
return loadIcon(LoadIcon.class, filename, size); return loadIcon(LoadIcon.class, filename, size);
} }

View File

@@ -26,6 +26,7 @@ public class BoxClassLoader extends URLClassLoader {
"java.", "javax.", "sun.", "com.sun.", "jdk.", "java.", "javax.", "sun.", "com.sun.", "jdk.",
"org.xml.", "org.w3c.", "org.apache.", "org.xml.", "org.w3c.", "org.apache.",
"javax.management.", "javax.swing." "javax.management.", "javax.swing."
, "javafx."
); );
} }

View File

@@ -64,11 +64,12 @@ public class ArgsParser {
public static void main(String[] args) { public static void main(String[] args) {
// 模拟 main 方法的 args 参数 // 模拟 main 方法的 args 参数
String[] testArgs = { String[] testArgs = {
"C:\\Program Files\\test.txt", // 合法文件 "C:\\Program Files\\test.txt",
"C:\\invalid_file.exe", // 不存在的文件 "C:\\invalid_file.exe",
"D:\\Documents\\report.pdf", // 合法文件 "D:\\Documents\\report.pdf",
"not_a_path", // 无效路径 "not_a_path",
"C:\\Windows\\system32" // 目录,不是文件 "C:\\Windows\\system32" ,
"C:\\Users\\Administrator\\MCreatorWorkspaces\\AxisInnovatorsBox\\build.gradle"
}; };
// 解析 args 参数 // 解析 args 参数

View File

@@ -0,0 +1,45 @@
.root {
-fx-base: #2D2D2D;
-fx-background: #1E1E1E;
-fx-control-inner-background: derive(-fx-base, 20%);
-fx-accent: #45A1FF;
}
.menu-bar {
-fx-background-color: linear-gradient(to bottom, #3C3C3C, #2D2D2D);
}
.tree-view {
-fx-background-color: -fx-base;
-fx-border-color: derive(-fx-base, -10%);
}
.tree-cell {
-fx-text-fill: #DCDCDC;
-fx-font-size: 14px;
}
.tree-cell:selected {
-fx-background-color: #45A1FF;
-fx-text-fill: white;
}
.code-area {
-fx-font-family: "JetBrains Mono";
-fx-font-size: 14px;
-fx-highlight-fill: #264F78;
}
.tab-pane {
-fx-background-color: derive(-fx-base, 10%);
}
.tab {
-fx-background-color: linear-gradient(to bottom, #3C3C3C, #2D2D2D);
-fx-text-fill: #DCDCDC;
}
.tab:selected {
-fx-background-color: #45A1FF;
-fx-text-fill: white;
}