feat(gui): 重构主界面并添加新功能
- 重新设计了主窗口布局,增加了侧边栏和内容面板 - 添加了分类按钮和设置按钮 - 实现了内存分析面板的自动补全功能 - 优化了日志文件生成和异常处理机制- 更新了依赖库版本
23
build.gradle
@@ -65,6 +65,8 @@ dependencies {
|
||||
//implementation 'com.formdev:flatlaf:0.26'
|
||||
implementation 'commons-io:commons-io:2.14.0'
|
||||
|
||||
|
||||
|
||||
implementation 'com.formdev:flatlaf:3.2.1' // FlatLaf核心
|
||||
implementation 'com.formdev:flatlaf-extras:3.2.1' // 扩展组件
|
||||
implementation 'com.formdev:flatlaf-intellij-themes:3.2.1' // 官方主题包
|
||||
@@ -74,6 +76,7 @@ dependencies {
|
||||
|
||||
implementation files('libs/JNC-1.0-jnc.jar')
|
||||
implementation files('libs/dog api 1.3.jar')
|
||||
implementation files('libs/DesktopWallpaperSdk-1.0-SNAPSHOT.jar')
|
||||
|
||||
implementation 'org.fxmisc.richtext:richtextfx:0.11.0' // 更新后的richtextfx
|
||||
implementation 'org.bitbucket.mstrobel:procyon-core:0.5.36' // 使用JitPack版本
|
||||
@@ -112,6 +115,21 @@ dependencies {
|
||||
//runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3', // JWT实现
|
||||
// 'io.jsonwebtoken:jjwt-jackson:0.12.3' // JWT序列化
|
||||
|
||||
implementation 'org.json:json:20230618'
|
||||
|
||||
implementation 'org.lwjgl:lwjgl:3.3.1'
|
||||
implementation 'org.lwjgl:lwjgl-glfw:3.3.1'
|
||||
implementation 'org.lwjgl:lwjgl-opengl:3.3.1'
|
||||
implementation 'org.lwjgl:lwjgl:3.3.1:natives-windows' // Windows natives
|
||||
implementation 'org.lwjgl:lwjgl-glfw:3.3.1:natives-windows'
|
||||
implementation 'org.lwjgl:lwjgl-opengl:3.3.1:natives-windows'
|
||||
|
||||
implementation 'org.bytedeco:javacv-platform:1.5.7' // JavaCV
|
||||
implementation 'org.bytedeco:javacpp-platform:1.5.7' // JavaCPP
|
||||
|
||||
implementation 'com.madgag:animated-gif-lib:1.4'
|
||||
implementation 'org.openjfx:javafx-web:17'
|
||||
|
||||
|
||||
runtimeOnly 'com.mysql:mysql-connector-j'
|
||||
|
||||
@@ -136,12 +154,15 @@ dependencies {
|
||||
implementation 'me.friwi:jcefmaven:122.1.10'
|
||||
|
||||
implementation 'com.alphacephei:vosk:0.3.45' // Java API
|
||||
implementation 'net.java.dev.jna:jna:5.12.1' // 本地库加载
|
||||
implementation 'net.java.dev.jna:jna:5.13.0'
|
||||
implementation 'net.java.dev.jna:jna-platform:5.13.0'
|
||||
|
||||
// 高性能音频处理
|
||||
implementation 'org.apache.commons:commons-math3:3.6.1'
|
||||
implementation 'com.google.guava:guava:31.1-jre'
|
||||
|
||||
implementation 'com.github.javaparser:javaparser-symbol-solver-core:3.25.9'
|
||||
|
||||
// 中文拼音处理
|
||||
implementation 'com.belerweb:pinyin4j:2.5.1'
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#Current Loaded Language
|
||||
#Sat May 31 10:30:35 CST 2025
|
||||
#Wed Jul 02 20:11:55 CST 2025
|
||||
loadedLanguage=system\:zh_CN
|
||||
|
||||
BIN
libs/DesktopWallpaperSdk-1.0-SNAPSHOT.jar
Normal file
@@ -17,6 +17,7 @@ import com.axis.innovators.box.util.Tray;
|
||||
import com.axis.innovators.box.util.UserLocalInformation;
|
||||
import com.axis.innovators.box.verification.UserTags;
|
||||
import com.formdev.flatlaf.FlatLightLaf;
|
||||
import com.sun.management.HotSpotDiagnosticMXBean;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
@@ -78,6 +79,10 @@ public class AxisInnovatorsBox {
|
||||
public AxisInnovatorsBox(String[] args, boolean isDebug) {
|
||||
this.args = args;
|
||||
this.isDebug = isDebug;
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
||||
organizingCrashReports(throwable instanceof Exception ?
|
||||
(Exception) throwable : new Exception(throwable));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,7 +363,7 @@ public class AxisInnovatorsBox {
|
||||
if (appender instanceof FileAppender fileAppender) {
|
||||
String fileName = fileAppender.getFileName();
|
||||
if (fileName != null && !addedFiles.contains(fileName)) {
|
||||
addFileToZip(zos, new File(fileName), "logs/");
|
||||
addFileToZip(zos, new File(fileName), "logs/" + new File(fileName).getName());
|
||||
addedFiles.add(fileName);
|
||||
}
|
||||
}
|
||||
@@ -387,7 +392,7 @@ public class AxisInnovatorsBox {
|
||||
for (File file : logFiles) {
|
||||
String absolutePath = file.getAbsolutePath();
|
||||
if (!addedFiles.contains(absolutePath)) {
|
||||
addFileToZip(zos, file, "logs/");
|
||||
addFileToZip(zos, file, "logs/" + file.getName());
|
||||
addedFiles.add(absolutePath);
|
||||
}
|
||||
}
|
||||
@@ -436,6 +441,24 @@ public class AxisInnovatorsBox {
|
||||
addFileToZip(zos, generateSystemProperties(), "debug_files/system_properties.txt");
|
||||
// 7. 添加环境变量
|
||||
addFileToZip(zos, generateEnvironmentVariables(), "debug_files/environment_variables.txt");
|
||||
// 8.生成dump文件
|
||||
addFileToZip(zos, generateFileDump(), "debug_files/dump.hprof");
|
||||
}
|
||||
|
||||
private File generateFileDump() throws IOException {
|
||||
final String javaLibraryPath = System.getProperty("java.library.path");
|
||||
String jvmFolder = javaLibraryPath.contains(";")
|
||||
? javaLibraryPath.substring(0, javaLibraryPath.indexOf(';'))
|
||||
: javaLibraryPath;
|
||||
logger.info("JVM folder: {}", jvmFolder);
|
||||
File dumpFile = File.createTempFile("dump", ".hprof");
|
||||
String dumpFilePath = dumpFile.getAbsolutePath();
|
||||
if (!dumpFile.delete()) {
|
||||
throw new IOException("Failed to delete temporary placeholder file");
|
||||
}
|
||||
HotSpotDiagnosticMXBean bean = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
|
||||
bean.dumpHeap(dumpFilePath, true);
|
||||
return new File(dumpFilePath);
|
||||
}
|
||||
|
||||
private File generateClassLoaderInfo() throws IOException {
|
||||
@@ -606,8 +629,7 @@ public class AxisInnovatorsBox {
|
||||
return;
|
||||
}
|
||||
|
||||
String entryName = entryPath + file.getName();
|
||||
ZipEntry zipEntry = new ZipEntry(entryName);
|
||||
ZipEntry zipEntry = new ZipEntry(entryPath);
|
||||
zos.putNextEntry(zipEntry);
|
||||
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
@@ -805,6 +827,22 @@ public class AxisInnovatorsBox {
|
||||
}
|
||||
|
||||
public static void run(String[] args, boolean isDebug) {
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
||||
if (main != null) {
|
||||
main.organizingCrashReports(throwable instanceof Exception ?
|
||||
(Exception) throwable : new Exception(throwable));
|
||||
} else {
|
||||
// 如果主类尚未初始化,创建临时实例处理崩溃
|
||||
new AxisInnovatorsBox(args, isDebug)
|
||||
.organizingCrashReports(throwable instanceof Exception ?
|
||||
(Exception) throwable : new Exception(throwable));
|
||||
}
|
||||
});
|
||||
|
||||
// 设置EDT(事件调度线程)的异常处理器
|
||||
System.setProperty("sun.awt.exception.handler", EDTCrashHandler.class.getName());
|
||||
|
||||
main = new AxisInnovatorsBox(args,isDebug);
|
||||
try {
|
||||
main.initLog4j2();
|
||||
@@ -880,7 +918,6 @@ public class AxisInnovatorsBox {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, "TrayThread").start();
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to load plugins", e);
|
||||
if (main.ex != null) {
|
||||
@@ -972,4 +1009,16 @@ public class AxisInnovatorsBox {
|
||||
public StateManager getStateManager() {
|
||||
return stateManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 专门处理EDT线程异常的处理器
|
||||
*/
|
||||
public static class EDTCrashHandler {
|
||||
public void handle(Throwable throwable) {
|
||||
if (main != null) {
|
||||
main.organizingCrashReports(throwable instanceof Exception ?
|
||||
(Exception) throwable : new Exception(throwable));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,10 +126,7 @@ public class Main {
|
||||
}
|
||||
}
|
||||
|
||||
// 添加JVM关闭钩子确保锁释放
|
||||
static {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
releaseLock();
|
||||
}));
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(Main::releaseLock));
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class CefAppManager {
|
||||
private static void initializeDefaultSettings() {
|
||||
initLock.lock();
|
||||
try {
|
||||
settings.windowless_rendering_enabled = false;
|
||||
settings.windowless_rendering_enabled = true;
|
||||
settings.javascript_flags = "--expose-gc";
|
||||
settings.cache_path = FolderCreator.getLibraryFolder() + "/jcef/cache";
|
||||
settings.root_cache_path = FolderCreator.getLibraryFolder() + "/jcef/cache";
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
package com.axis.innovators.box.events;
|
||||
|
||||
import com.axis.innovators.box.gui.MainWindow;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* 分类栏的渲染事件
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class CategoryRenderingEvent {
|
||||
private final MainWindow.CustomTabbedPaneUI ui;
|
||||
private final Graphics graphics;
|
||||
private final int tabPlacement;
|
||||
private final int tabIndex;
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final boolean isSelected;
|
||||
private boolean isEnd = false;
|
||||
|
||||
public CategoryRenderingEvent(MainWindow.CustomTabbedPaneUI ui, Graphics graphics, int tabPlacement, int tabIndex, int x, int y, int width, int height, boolean isSelected) {
|
||||
this.ui = ui;
|
||||
this.graphics = graphics;
|
||||
this.tabPlacement = tabPlacement;
|
||||
this.tabIndex = tabIndex;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.isSelected = isSelected;
|
||||
}
|
||||
|
||||
public MainWindow.CustomTabbedPaneUI getUi() {
|
||||
return ui;
|
||||
}
|
||||
|
||||
public Graphics getGraphics() {
|
||||
return graphics;
|
||||
}
|
||||
|
||||
public int getTabPlacement() {
|
||||
return tabPlacement;
|
||||
}
|
||||
|
||||
public int getTabIndex() {
|
||||
return tabIndex;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return isSelected;
|
||||
}
|
||||
|
||||
public boolean isEnd() {
|
||||
return isEnd;
|
||||
}
|
||||
|
||||
public void setEnd(boolean end) {
|
||||
isEnd = end;
|
||||
}
|
||||
|
||||
public static class paintTabBorder {
|
||||
private final MainWindow.CustomTabbedPaneUI event;
|
||||
private final Graphics graphics;
|
||||
private final int tabPlacement;
|
||||
private final int tabIndex;
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final boolean isSelected;
|
||||
|
||||
public paintTabBorder(MainWindow.CustomTabbedPaneUI event, Graphics graphics, int tabPlacement, int tabIndex, int x, int y, int width, int height, boolean isSelected) {
|
||||
this.event = event;
|
||||
this.graphics = graphics;
|
||||
this.tabPlacement = tabPlacement;
|
||||
this.tabIndex = tabIndex;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.isSelected = isSelected;
|
||||
}
|
||||
|
||||
public MainWindow.CustomTabbedPaneUI getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public Graphics getGraphics() {
|
||||
return graphics;
|
||||
}
|
||||
|
||||
public int getTabPlacement() {
|
||||
return tabPlacement;
|
||||
}
|
||||
|
||||
public int getTabIndex() {
|
||||
return tabIndex;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return isSelected;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ public class MainWindow extends JFrame {
|
||||
private final Color CARD_COLOR = Color.WHITE;
|
||||
private final List<ToolCategory> categories = new ArrayList<>();
|
||||
private SystemTray systemTray;
|
||||
private JPanel contentPanel;
|
||||
//private TrayIcon trayIcon;
|
||||
|
||||
public MainWindow() {
|
||||
@@ -67,7 +68,7 @@ public class MainWindow extends JFrame {
|
||||
|
||||
getContentPane().setBackground(new Color(0, 0, 0, 0));
|
||||
|
||||
JPanel mainPanel = new JPanel();
|
||||
JPanel mainPanel = new JPanel(new BorderLayout());
|
||||
|
||||
mainPanel.setOpaque(true);
|
||||
|
||||
@@ -86,8 +87,87 @@ public class MainWindow extends JFrame {
|
||||
setVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
JPanel sideBar = createSideBar();
|
||||
mainPanel.add(sideBar, BorderLayout.WEST);
|
||||
|
||||
contentPanel = new JPanel(new BorderLayout());
|
||||
contentPanel.setOpaque(false);
|
||||
mainPanel.add(contentPanel, BorderLayout.CENTER);
|
||||
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
|
||||
|
||||
add(mainPanel);
|
||||
|
||||
}
|
||||
|
||||
private JPanel createSideBar() {
|
||||
JPanel sidebar = new JPanel();
|
||||
sidebar.setLayout(new BoxLayout(sidebar, BoxLayout.Y_AXIS));
|
||||
sidebar.setBackground(new Color(35, 35, 35));
|
||||
sidebar.setPreferredSize(new Dimension(180, getHeight()));
|
||||
|
||||
// 添加分类按钮
|
||||
for (ToolCategory category : categories) {
|
||||
JButton button = new JButton(category.getName());
|
||||
button.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
button.setMaximumSize(new Dimension(160, 40));
|
||||
button.setForeground(Color.WHITE);
|
||||
button.setBackground(new Color(60, 60, 60));
|
||||
button.setFocusPainted(false);
|
||||
button.setBorderPainted(false);
|
||||
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
|
||||
button.addActionListener(e -> {
|
||||
JPanel toolsPanel = createToolsPanel(category);
|
||||
toolsPanel.setOpaque(false);
|
||||
contentPanel.removeAll();
|
||||
JScrollPane scrollPane = new JScrollPane(toolsPanel);
|
||||
scrollPane.setBorder(null);
|
||||
scrollPane.setOpaque(false);
|
||||
scrollPane.getViewport().setOpaque(false);
|
||||
scrollPane.getVerticalScrollBar().setUI(new CustomScrollBarUI());
|
||||
contentPanel.add(scrollPane, BorderLayout.CENTER);
|
||||
contentPanel.revalidate();
|
||||
contentPanel.repaint();
|
||||
});
|
||||
|
||||
sidebar.add(Box.createVerticalStrut(10));
|
||||
sidebar.add(button);
|
||||
}
|
||||
|
||||
// 添加设置按钮
|
||||
JButton settingsButton = new JButton("设置");
|
||||
settingsButton.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
settingsButton.setMaximumSize(new Dimension(160, 40));
|
||||
settingsButton.setForeground(Color.WHITE);
|
||||
settingsButton.setBackground(new Color(45, 45, 45));
|
||||
settingsButton.setFocusPainted(false);
|
||||
settingsButton.setBorderPainted(false);
|
||||
settingsButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
settingsButton.addActionListener(e -> showSettings());
|
||||
sidebar.add(Box.createVerticalGlue());
|
||||
sidebar.add(settingsButton);
|
||||
|
||||
// 添加关于按钮
|
||||
JButton aboutButton = new JButton("关于");
|
||||
aboutButton.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
aboutButton.setMaximumSize(new Dimension(160, 40));
|
||||
aboutButton.setForeground(Color.WHITE);
|
||||
aboutButton.setBackground(new Color(45, 45, 45));
|
||||
aboutButton.setFocusPainted(false);
|
||||
aboutButton.setBorderPainted(false);
|
||||
aboutButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
aboutButton.addActionListener(e -> JOptionPane.showMessageDialog(this, "作者: tzdwindows7"));
|
||||
sidebar.add(Box.createVerticalStrut(10));
|
||||
sidebar.add(aboutButton);
|
||||
|
||||
return sidebar;
|
||||
}
|
||||
|
||||
|
||||
private JPanel createFooter() {
|
||||
JPanel footer = new JPanel();
|
||||
footer.setLayout(new BoxLayout(footer, BoxLayout.X_AXIS));
|
||||
@@ -652,11 +732,6 @@ public class MainWindow extends JFrame {
|
||||
protected void paintTabBackground(Graphics g, int tabPlacement,
|
||||
int tabIndex, int x, int y, int w, int h,
|
||||
boolean isSelected) {
|
||||
CategoryRenderingEvent event = new CategoryRenderingEvent(this, g, tabPlacement, tabIndex, x, y, w, h, isSelected);
|
||||
GlobalEventBus.EVENT_BUS.post(event);
|
||||
if (event.isEnd()){
|
||||
return;
|
||||
}
|
||||
Graphics2D g2d = (Graphics2D) g.create();
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
@@ -669,15 +744,6 @@ public class MainWindow extends JFrame {
|
||||
g2d.fillRoundRect(x + 2, y + 2, w - 4, h - 4, 10, 10);
|
||||
g2d.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintTabBorder(Graphics g, int tabPlacement,
|
||||
int tabIndex, int x, int y, int w, int h,
|
||||
boolean isSelected) {
|
||||
GlobalEventBus.EVENT_BUS.post(new CategoryRenderingEvent.paintTabBorder(this, g,
|
||||
tabPlacement,
|
||||
tabIndex, x, y, w, h, isSelected));
|
||||
}
|
||||
}
|
||||
|
||||
private class CardMouseAdapter extends MouseAdapter {
|
||||
|
||||
@@ -14,9 +14,7 @@ import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.table.JTableHeader;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.management.ManagementFactory;
|
||||
@@ -30,7 +28,7 @@ import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class MemoryAnalysisPanel extends JPanel {
|
||||
private static final DecimalFormat MB_FORMAT = new DecimalFormat("#,##0.00");
|
||||
@@ -92,6 +90,14 @@ public class MemoryAnalysisPanel extends JPanel {
|
||||
private final JTable specificClassInstanceTable;
|
||||
private final DefaultTableModel specificClassInstanceModel;
|
||||
|
||||
// 自动补全相关组件
|
||||
private JPopupMenu autoCompletePopup;
|
||||
private JList<String> autoCompleteList;
|
||||
private DefaultListModel<String> autoCompleteModel;
|
||||
private ScheduledExecutorService autoCompleteExecutor;
|
||||
private final Map<String, List<String>> classCache = new ConcurrentHashMap<>();
|
||||
private final Set<String> loadedPackages = ConcurrentHashMap.newKeySet();
|
||||
|
||||
// 特定类分析面板组件
|
||||
private JPanel visualizationPanel;
|
||||
private JLabel ratioLabel;
|
||||
@@ -99,6 +105,10 @@ public class MemoryAnalysisPanel extends JPanel {
|
||||
private JTextField instanceSearchField;
|
||||
private TableRowSorter<DefaultTableModel> instanceSorter;
|
||||
|
||||
// 可调整大小的分割面板
|
||||
private JSplitPane mainSplitPane;
|
||||
private JSplitPane infoVisualizationSplitPane;
|
||||
private JSplitPane visualizationInstanceSplitPane;
|
||||
|
||||
public MemoryAnalysisPanel() {
|
||||
super(new BorderLayout());
|
||||
@@ -280,6 +290,231 @@ public class MemoryAnalysisPanel extends JPanel {
|
||||
// 初始刷新 - 只加载内存使用和内存池数据
|
||||
showLoadingDialog("正在初始化内存数据...");
|
||||
refreshInitialData();
|
||||
|
||||
// 初始化自动补全系统
|
||||
initAutoCompleteSystem();
|
||||
}
|
||||
|
||||
private void initAutoCompleteSystem() {
|
||||
// 创建自动补全组件
|
||||
autoCompleteModel = new DefaultListModel<>();
|
||||
autoCompleteList = new JList<>(autoCompleteModel);
|
||||
autoCompleteList.setFont(DebugWindow.MONOSPACE_FONT);
|
||||
autoCompleteList.setBackground(new Color(50, 50, 50));
|
||||
autoCompleteList.setForeground(FOREGROUND);
|
||||
autoCompleteList.setSelectionBackground(ACCENT);
|
||||
autoCompleteList.setSelectionForeground(FOREGROUND);
|
||||
autoCompleteList.setFixedCellHeight(25);
|
||||
|
||||
autoCompletePopup = new JPopupMenu();
|
||||
autoCompletePopup.setBorder(BorderFactory.createLineBorder(new Color(80, 80, 80)));
|
||||
autoCompletePopup.add(new JScrollPane(autoCompleteList));
|
||||
|
||||
// 添加选择监听器
|
||||
autoCompleteList.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() == 1) {
|
||||
selectAutoCompleteItem();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 添加键盘监听器
|
||||
specificClassField.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
handleAutoCompleteKeyPress(e);
|
||||
}
|
||||
});
|
||||
|
||||
// 添加文档监听器
|
||||
specificClassField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
scheduleAutoCompleteUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
scheduleAutoCompleteUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
scheduleAutoCompleteUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化线程池
|
||||
autoCompleteExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
}
|
||||
|
||||
private void scheduleAutoCompleteUpdate() {
|
||||
// 取消之前的任务
|
||||
autoCompleteExecutor.shutdownNow();
|
||||
autoCompleteExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
// 安排新任务
|
||||
autoCompleteExecutor.schedule(() -> {
|
||||
SwingUtilities.invokeLater(this::updateAutoCompleteSuggestions);
|
||||
}, 300, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void updateAutoCompleteSuggestions() {
|
||||
String text = specificClassField.getText().trim();
|
||||
if (text.isEmpty()) {
|
||||
autoCompletePopup.setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取或加载建议
|
||||
List<String> suggestions = getAutoCompleteSuggestions(text);
|
||||
autoCompleteModel.clear();
|
||||
|
||||
if (suggestions.isEmpty()) {
|
||||
autoCompletePopup.setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加到模型
|
||||
for (String suggestion : suggestions) {
|
||||
autoCompleteModel.addElement(suggestion);
|
||||
}
|
||||
|
||||
// 显示弹出菜单
|
||||
if (!autoCompletePopup.isVisible()) {
|
||||
showAutoCompletePopup();
|
||||
}
|
||||
|
||||
// 选择第一个项目
|
||||
autoCompleteList.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
private List<String> getAutoCompleteSuggestions(String input) {
|
||||
String packagePrefix = input.contains(".") ?
|
||||
input.substring(0, input.lastIndexOf('.') + 1) :
|
||||
"";
|
||||
|
||||
// 如果包名部分发生变化,重新加载该包下的类
|
||||
if (!classCache.containsKey(packagePrefix) || !loadedPackages.contains(packagePrefix)) {
|
||||
loadPackageClasses(packagePrefix);
|
||||
}
|
||||
|
||||
List<String> allClasses = classCache.getOrDefault(packagePrefix, new ArrayList<>());
|
||||
String searchTerm = input.toLowerCase();
|
||||
|
||||
// 过滤匹配的类
|
||||
List<String> suggestions = new ArrayList<>();
|
||||
for (String className : allClasses) {
|
||||
if (className.toLowerCase().contains(searchTerm)) {
|
||||
suggestions.add(className);
|
||||
}
|
||||
|
||||
// 限制数量
|
||||
if (suggestions.size() >= 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 按匹配度排序
|
||||
suggestions.sort((s1, s2) -> {
|
||||
int pos1 = s1.toLowerCase().indexOf(searchTerm);
|
||||
int pos2 = s2.toLowerCase().indexOf(searchTerm);
|
||||
return Integer.compare(pos1, pos2);
|
||||
});
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
private void loadPackageClasses(String packageName) {
|
||||
// 对于根包的特殊处理
|
||||
if (packageName.isEmpty()) {
|
||||
packageName = "";
|
||||
}
|
||||
|
||||
List<String> classes = new ArrayList<>();
|
||||
Class<?>[] allClasses = instrumentation.getAllLoadedClasses();
|
||||
|
||||
for (Class<?> clazz : allClasses) {
|
||||
String className = clazz.getName();
|
||||
|
||||
// 只处理包匹配的类
|
||||
if (packageName.isEmpty() || className.startsWith(packageName)) {
|
||||
classes.add(className);
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存结果
|
||||
classCache.put(packageName, classes);
|
||||
loadedPackages.add(packageName);
|
||||
}
|
||||
|
||||
private void showAutoCompletePopup() {
|
||||
if (specificClassField.isShowing()) {
|
||||
// 计算弹出位置
|
||||
Point location = specificClassField.getLocationOnScreen();
|
||||
location.y += specificClassField.getHeight();
|
||||
|
||||
// 设置弹出大小
|
||||
int width = Math.max(specificClassField.getWidth(), 400);
|
||||
int height = Math.min(autoCompleteModel.size() * 25, 300);
|
||||
autoCompletePopup.setPopupSize(width, height);
|
||||
|
||||
// 显示弹出菜单
|
||||
autoCompletePopup.show(specificClassField, 0, specificClassField.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAutoCompleteKeyPress(KeyEvent e) {
|
||||
if (!autoCompletePopup.isVisible()) return;
|
||||
|
||||
switch (e.getKeyCode()) {
|
||||
case KeyEvent.VK_UP:
|
||||
moveSelection(-1);
|
||||
e.consume();
|
||||
break;
|
||||
case KeyEvent.VK_DOWN:
|
||||
moveSelection(1);
|
||||
e.consume();
|
||||
break;
|
||||
case KeyEvent.VK_ENTER:
|
||||
selectAutoCompleteItem();
|
||||
e.consume();
|
||||
break;
|
||||
case KeyEvent.VK_ESCAPE:
|
||||
autoCompletePopup.setVisible(false);
|
||||
e.consume();
|
||||
break;
|
||||
case KeyEvent.VK_TAB:
|
||||
if (autoCompleteList.getSelectedIndex() >= 0) {
|
||||
selectAutoCompleteItem();
|
||||
e.consume();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void moveSelection(int direction) {
|
||||
int selected = autoCompleteList.getSelectedIndex();
|
||||
int newIndex = selected + direction;
|
||||
|
||||
if (newIndex >= 0 && newIndex < autoCompleteModel.size()) {
|
||||
autoCompleteList.setSelectedIndex(newIndex);
|
||||
autoCompleteList.ensureIndexIsVisible(newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void selectAutoCompleteItem() {
|
||||
String selected = autoCompleteList.getSelectedValue();
|
||||
if (selected != null) {
|
||||
specificClassField.setText(selected);
|
||||
autoCompletePopup.setVisible(false);
|
||||
specificClassField.requestFocus();
|
||||
|
||||
// 将光标移动到文本末尾
|
||||
specificClassField.setCaretPosition(selected.length());
|
||||
}
|
||||
}
|
||||
|
||||
private JPanel createSpecificClassAnalysisPanel() {
|
||||
@@ -316,41 +551,44 @@ public class MemoryAnalysisPanel extends JPanel {
|
||||
inputPanel.add(classLabel, BorderLayout.WEST);
|
||||
inputPanel.add(fieldPanel, BorderLayout.CENTER);
|
||||
|
||||
// 主内容面板 (使用网格袋布局)
|
||||
JPanel mainContentPanel = new JPanel(new GridBagLayout());
|
||||
mainContentPanel.setBackground(BACKGROUND);
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.fill = GridBagConstraints.BOTH;
|
||||
gbc.weightx = 1.0;
|
||||
gbc.insets = new Insets(5, 5, 5, 5);
|
||||
// 使用分割面板替代网格袋布局
|
||||
mainSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
||||
mainSplitPane.setDividerLocation(0.4); // 初始比例为40%
|
||||
mainSplitPane.setResizeWeight(0.4);
|
||||
mainSplitPane.setBorder(BorderFactory.createEmptyBorder());
|
||||
mainSplitPane.setContinuousLayout(true);
|
||||
mainSplitPane.setDividerSize(5);
|
||||
mainSplitPane.setBackground(BACKGROUND);
|
||||
|
||||
// 类信息面板
|
||||
// 上半部分:类信息面板
|
||||
JPanel infoPanel = createClassInfoPanel();
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.weighty = 0.4;
|
||||
mainContentPanel.add(infoPanel, gbc);
|
||||
infoPanel.setMinimumSize(new Dimension(100, 100));
|
||||
|
||||
// 下半部分:可视化+实例列表
|
||||
JSplitPane bottomSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
||||
bottomSplitPane.setDividerLocation(0.3); // 初始比例为30%
|
||||
bottomSplitPane.setResizeWeight(0.3);
|
||||
bottomSplitPane.setContinuousLayout(true);
|
||||
bottomSplitPane.setDividerSize(5);
|
||||
bottomSplitPane.setBackground(BACKGROUND);
|
||||
|
||||
// 可视化面板
|
||||
visualizationPanel = createVisualizationPanel();
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
gbc.gridwidth = 1;
|
||||
gbc.weighty = 0.1;
|
||||
mainContentPanel.add(visualizationPanel, gbc);
|
||||
visualizationPanel.setMinimumSize(new Dimension(100, 100));
|
||||
|
||||
// 实例列表面板
|
||||
JPanel instancesPanel = createInstancesPanel();
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 1;
|
||||
gbc.gridwidth = 1;
|
||||
gbc.weighty = 0.5;
|
||||
mainContentPanel.add(instancesPanel, gbc);
|
||||
instancesPanel.setMinimumSize(new Dimension(100, 100));
|
||||
|
||||
bottomSplitPane.setLeftComponent(visualizationPanel);
|
||||
bottomSplitPane.setRightComponent(instancesPanel);
|
||||
|
||||
mainSplitPane.setTopComponent(infoPanel);
|
||||
mainSplitPane.setBottomComponent(bottomSplitPane);
|
||||
|
||||
// 添加主内容面板到中心
|
||||
panel.add(inputPanel, BorderLayout.NORTH);
|
||||
panel.add(mainContentPanel, BorderLayout.CENTER);
|
||||
panel.add(mainSplitPane, BorderLayout.CENTER);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ import java.nio.file.Paths;
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
HighAccuracySpeechRecognition recognizer = new HighAccuracySpeechRecognition(
|
||||
"C:\\Users\\Administrator\\Desktop\\声音识别模型\\vosk-model-cn-0.22",
|
||||
"output"
|
||||
);
|
||||
|
||||
recognizer.processAudio(Paths.get("G:\\鬼畜素材\\工作间\\哪吒-嗵嗵\\哪吒-嗵嗵1.wav"));
|
||||
//HighAccuracySpeechRecognition recognizer = new HighAccuracySpeechRecognition(
|
||||
// "C:\\Users\\Administrator\\Desktop\\声音识别模型\\vosk-model-cn-0.22",
|
||||
// "output"
|
||||
//);
|
||||
//
|
||||
//recognizer.processAudio(Paths.get("G:\\鬼畜素材\\工作间\\哪吒-嗵嗵\\哪吒-嗵嗵1.wav"));
|
||||
}
|
||||
}
|
||||
|
||||
4
src/main/java/org/tzd/explorer/DesktopIconRenderer.java
Normal file
@@ -0,0 +1,4 @@
|
||||
package org.tzd.explorer;
|
||||
|
||||
public class DesktopIconRenderer {
|
||||
}
|
||||
BIN
src/main/resources/icons/programming/JarApiViewer/cfg_file.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
src/main/resources/icons/programming/JarApiViewer/exe_file.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
src/main/resources/icons/programming/JarApiViewer/file.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
src/main/resources/icons/programming/JarApiViewer/file_jar.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/main/resources/icons/programming/JarApiViewer/java_file.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 92 KiB |
BIN
src/main/resources/icons/programming/JarApiViewer/toml_file.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
src/main/resources/icons/programming/JarApiViewer/zip_file.png
Normal file
|
After Width: | Height: | Size: 22 KiB |