第一次提交
This commit is contained in:
70
src/main/java/com/axis/innovators/box/Main.java
Normal file
70
src/main/java/com/axis/innovators/box/Main.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package com.axis.innovators.box;
|
||||
|
||||
import com.axis.innovators.box.events.GlobalEventBus;
|
||||
import com.axis.innovators.box.events.SettingsLoadEvents;
|
||||
import com.axis.innovators.box.events.SubscribeEvent;
|
||||
import com.axis.innovators.box.gui.FridaWindow;
|
||||
import com.axis.innovators.box.gui.MainWindow;
|
||||
import com.axis.innovators.box.tools.FolderCreator;
|
||||
import com.axis.innovators.box.tools.LibraryLoad;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
/**
|
||||
* 主类
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
static {
|
||||
LibraryLoad.loadLibrary("FridaNative");
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onSettingsLoad(SettingsLoadEvents event) {
|
||||
JLabel placeholder = new JLabel("设置功能开发中...", SwingConstants.CENTER);
|
||||
placeholder.setFont(new Font("微软雅黑", Font.PLAIN, 24));
|
||||
placeholder.setForeground(new Color(127, 140, 153));
|
||||
event.content().add(placeholder, BorderLayout.CENTER);
|
||||
|
||||
// 我不想写这个了你们自己实现
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 注册事件
|
||||
GlobalEventBus.EVENT_BUS.register(new Main());
|
||||
// 设置系统外观
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
// 创建窗口
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
MainWindow ex = new MainWindow();
|
||||
int id = 0;
|
||||
MainWindow.ToolCategory debugCategory = new MainWindow.ToolCategory("调试工具",
|
||||
"debug/debug.png",
|
||||
"用于调试指定Windows工具的一个分类");
|
||||
|
||||
debugCategory.addTool(new MainWindow.ToolItem("Frida注入工具", "debug/frida/frida_main.png",
|
||||
"使用frida注入目标进程的脚本程序 " +
|
||||
"\n作者:tzdwindows 7", ++id, new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
|
||||
FridaWindow fridaWindow = new FridaWindow(owner);
|
||||
fridaWindow.setVisible(true);
|
||||
}
|
||||
}));
|
||||
|
||||
// 在后面注册你自己的项或是添加你自己的分类
|
||||
// ....
|
||||
|
||||
ex.addToolCategory(debugCategory);
|
||||
ex.initUI();
|
||||
ex.setVisible(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
150
src/main/java/com/axis/innovators/box/events/EventBus.java
Normal file
150
src/main/java/com/axis/innovators/box/events/EventBus.java
Normal file
@@ -0,0 +1,150 @@
|
||||
package com.axis.innovators.box.events;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 事件总线
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class EventBus {
|
||||
private static int maxID = 0;
|
||||
private final int busID;
|
||||
private final Map<Class<?>, List<Subscriber>> eventSubscribers = new HashMap<>();
|
||||
private final Map<Object, List<Subscriber>> targetSubscribers = new HashMap<>();
|
||||
private boolean shutdown;
|
||||
|
||||
public EventBus() {
|
||||
this.busID = maxID++;
|
||||
}
|
||||
|
||||
private static class Subscriber {
|
||||
final Object target;
|
||||
final Method method;
|
||||
final Class<?> eventType;
|
||||
|
||||
Subscriber(Object target, Method method, Class<?> eventType) {
|
||||
this.target = target;
|
||||
this.method = method;
|
||||
this.eventType = eventType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册目标对象的事件监听器
|
||||
* @param target 目标对象
|
||||
*/
|
||||
public void register(Object target) {
|
||||
if (targetSubscribers.containsKey(target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Subscriber> subs = new ArrayList<>();
|
||||
for (Method method : getAnnotatedMethods(target)) {
|
||||
SubscribeEvent annotation = method.getAnnotation(SubscribeEvent.class);
|
||||
if (annotation == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Class<?> eventType = paramTypes[0];
|
||||
Subscriber sub = new Subscriber(target, method, eventType);
|
||||
eventSubscribers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(sub);
|
||||
subs.add(sub);
|
||||
}
|
||||
|
||||
if (!subs.isEmpty()) {
|
||||
targetSubscribers.put(target, subs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目标对象中所有带有 @SubscribeEvent 注解的方法
|
||||
* @param target 目标对象
|
||||
* @return 方法集合
|
||||
*/
|
||||
private Set<Method> getAnnotatedMethods(Object target) {
|
||||
Set<Method> methods = new HashSet<>();
|
||||
Class<?> clazz = target.getClass();
|
||||
while (clazz != null) {
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
if (method.isAnnotationPresent(SubscribeEvent.class)) {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销目标对象的事件监听器
|
||||
* @param target 目标对象
|
||||
*/
|
||||
public void unregister(Object target) {
|
||||
List<Subscriber> subs = targetSubscribers.remove(target);
|
||||
if (subs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Subscriber sub : subs) {
|
||||
List<Subscriber> eventSubs = eventSubscribers.get(sub.eventType);
|
||||
if (eventSubs != null) {
|
||||
eventSubs.remove(sub);
|
||||
if (eventSubs.isEmpty()) {
|
||||
eventSubscribers.remove(sub.eventType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布事件
|
||||
* @param event 事件对象
|
||||
* @return 返回事件是否被取消的状态
|
||||
*/
|
||||
public boolean post(Object event) {
|
||||
if (shutdown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean cancelled = false;
|
||||
Class<?> eventType = event.getClass();
|
||||
List<Subscriber> subs = eventSubscribers.get(eventType);
|
||||
if (subs == null) {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
// 创建副本以避免并发修改异常
|
||||
List<Subscriber> copySubs = new ArrayList<>(subs);
|
||||
for (Subscriber sub : copySubs) {
|
||||
try {
|
||||
sub.method.setAccessible(true);
|
||||
sub.method.invoke(sub.target, event);
|
||||
} catch (Exception e) {
|
||||
handleException(event, e);
|
||||
}
|
||||
}
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭事件总线,停止处理事件
|
||||
*/
|
||||
public void shutdown() {
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理事件处理过程中出现的异常
|
||||
* @param event 事件
|
||||
* @param e 异常
|
||||
*/
|
||||
private void handleException(Object event, Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.axis.innovators.box.events;
|
||||
|
||||
/**
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class GlobalEventBus {
|
||||
public static final EventBus EVENT_BUS = new EventBus();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.axis.innovators.box.events;
|
||||
|
||||
import com.axis.innovators.box.gui.MainWindow;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* 主窗口事件
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class MainWindowEvents {
|
||||
|
||||
/**
|
||||
* 在更新主窗口时调用
|
||||
*/
|
||||
public record update(MainWindow mainWindow, Graphics g) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 在初始化主窗口时调用
|
||||
*/
|
||||
public record initialize(MainWindow mainWindow, JPanel mainPanel) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.axis.innovators.box.events;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* 当设置被初始化加载时被调用
|
||||
* 可以在这里添加新的设置选项
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public record SettingsLoadEvents(JDialog dialog, JPanel content) {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.axis.innovators.box.events;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* 事件注解,事件订阅注解,用于标记事件监听方法
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SubscribeEvent {
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.axis.innovators.box.events;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* 选项卡Ui事件,当设置选项卡Ui属性时被调用
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class TABUIEvents {
|
||||
private final JComponent javax;
|
||||
private final JPanel card;
|
||||
|
||||
public TABUIEvents(JPanel card, JComponent c){
|
||||
this.javax = c;
|
||||
this.card = card;
|
||||
}
|
||||
|
||||
public JComponent getJavax() {
|
||||
return javax;
|
||||
}
|
||||
|
||||
public JPanel getCard() {
|
||||
return card;
|
||||
}
|
||||
|
||||
/**
|
||||
* 选项卡更新事件
|
||||
*/
|
||||
public static class update {
|
||||
private final JComponent javax;
|
||||
private final Graphics graphics;
|
||||
|
||||
public update(Graphics g, JComponent c){
|
||||
this.javax = c;
|
||||
this.graphics = g;
|
||||
}
|
||||
|
||||
public JComponent getJavax() {
|
||||
return javax;
|
||||
}
|
||||
|
||||
public Graphics getGraphics() {
|
||||
return graphics;
|
||||
}
|
||||
}
|
||||
}
|
||||
298
src/main/java/com/axis/innovators/box/gui/FridaWindow.java
Normal file
298
src/main/java/com/axis/innovators/box/gui/FridaWindow.java
Normal file
@@ -0,0 +1,298 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
|
||||
import org.tzd.frida.windows.CallbackMessage;
|
||||
import org.tzd.frida.windows.Frida;
|
||||
import org.tzd.frida.windows.FridaRunnable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FridaWindow extends JDialog {
|
||||
private JTextArea scriptArea;
|
||||
private JTextArea logArea;
|
||||
private JTextField pidField;
|
||||
private boolean isRepetition = false;
|
||||
|
||||
public FridaWindow(Window owner) {
|
||||
super(owner, "Frida 注入工具", ModalityType.APPLICATION_MODAL);
|
||||
initializeUI();
|
||||
}
|
||||
|
||||
private void initializeUI() {
|
||||
setSize(800, 600);
|
||||
setLocationRelativeTo(getOwner());
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
|
||||
JPanel mainPanel = new JPanel() {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
Color color1 = new Color(235, 241, 250);
|
||||
Color color2 = new Color(255, 255, 255);
|
||||
GradientPaint gp = new GradientPaint(0, 0, color1, getWidth(), getHeight(), color2);
|
||||
g2d.setPaint(gp);
|
||||
g2d.fillRect(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
};
|
||||
mainPanel.setLayout(new BorderLayout(10, 10));
|
||||
mainPanel.setBorder(new EmptyBorder(15, 15, 15, 15));
|
||||
|
||||
// 输入面板
|
||||
JPanel inputPanel = createInputPanel();
|
||||
mainPanel.add(inputPanel, BorderLayout.NORTH);
|
||||
|
||||
// 脚本编辑区
|
||||
JPanel scriptPanel = createScriptPanel();
|
||||
mainPanel.add(scriptPanel, BorderLayout.CENTER);
|
||||
|
||||
// 日志输出
|
||||
JPanel logPanel = createLogPanel();
|
||||
mainPanel.add(logPanel, BorderLayout.SOUTH);
|
||||
|
||||
setContentPane(mainPanel);
|
||||
}
|
||||
|
||||
private JPanel createInputPanel() {
|
||||
JPanel panel = new JPanel(new GridLayout(1, 3, 10, 10));
|
||||
panel.setOpaque(false);
|
||||
|
||||
pidField = new JTextField("请输入进程PID");
|
||||
pidField.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
||||
pidField.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(new Color(57, 56, 56)),
|
||||
BorderFactory.createEmptyBorder(5, 10, 5, 10)
|
||||
));
|
||||
|
||||
//JButton attachButton = new JButton("附加进程");
|
||||
//styleButton(attachButton, new Color(70, 130, 180));
|
||||
//attachButton.setEnabled(false);
|
||||
|
||||
JButton browseButton = new JButton("选择进程");
|
||||
styleButton(browseButton, new Color(60, 179, 113));
|
||||
browseButton.addActionListener(this::openProcessSelectionWindow);
|
||||
panel.add(pidField);
|
||||
//panel.add(attachButton);
|
||||
panel.add(browseButton);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createScriptPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setOpaque(false);
|
||||
panel.setBorder(BorderFactory.createTitledBorder("脚本编辑器"));
|
||||
|
||||
scriptArea = new JTextArea();
|
||||
scriptArea.setFont(new Font("Consolas", Font.PLAIN, 14));
|
||||
JScrollPane scrollPane = new JScrollPane(scriptArea);
|
||||
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0)));
|
||||
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 5));
|
||||
buttonPanel.setOpaque(false);
|
||||
|
||||
JButton injectButton = new JButton("注入脚本");
|
||||
styleButton(injectButton, new Color(220, 20, 60));
|
||||
injectButton.addActionListener(this::handleInject);
|
||||
|
||||
buttonPanel.add(injectButton);
|
||||
|
||||
panel.add(scrollPane, BorderLayout.CENTER);
|
||||
panel.add(buttonPanel, BorderLayout.SOUTH);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createLogPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setPreferredSize(new Dimension(0, 150));
|
||||
panel.setBorder(BorderFactory.createTitledBorder("日志输出"));
|
||||
panel.setOpaque(false);
|
||||
|
||||
logArea = new JTextArea();
|
||||
logArea.setEditable(false);
|
||||
logArea.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
||||
JScrollPane scrollPane = new JScrollPane(logArea);
|
||||
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0)));
|
||||
|
||||
panel.add(scrollPane);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void styleButton(JButton button, Color bgColor) {
|
||||
button.setFont(new Font("微软雅黑", Font.BOLD, 14));
|
||||
button.setFocusPainted(false);
|
||||
button.setBackground(bgColor);
|
||||
button.setForeground(Color.black);
|
||||
button.setBorder(BorderFactory.createEmptyBorder(8, 20, 8, 20));
|
||||
}
|
||||
|
||||
private void handleInject(ActionEvent e) {
|
||||
try {
|
||||
long pid = Long.parseLong(pidField.getText());
|
||||
String script = scriptArea.getText();
|
||||
|
||||
Frida frida = new Frida(script, pid);
|
||||
if (!isRepetition) {
|
||||
frida.run(() -> {
|
||||
// 执行注入操作
|
||||
}).execute(frida1 -> frida1.addCallbackMessage(message ->
|
||||
SwingUtilities.invokeLater(() ->
|
||||
logArea.append("[LOG] " + message + "\n")
|
||||
)
|
||||
)).start();
|
||||
isRepetition = true;
|
||||
} else {
|
||||
frida.run(() -> {}).execute(frida12 -> {}).start();
|
||||
}
|
||||
|
||||
} catch (NumberFormatException ex) {
|
||||
JOptionPane.showMessageDialog(this, "无效的进程ID", "错误", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private void openProcessSelectionWindow(ActionEvent e) {
|
||||
// 打开一个窗口,显示所有进程的ID、名称和图标
|
||||
ProcessSelectionWindow selectionWindow = new ProcessSelectionWindow(this);
|
||||
selectionWindow.setVisible(true);
|
||||
}
|
||||
|
||||
// 选择进程的子窗口
|
||||
private static class ProcessSelectionWindow extends JDialog {
|
||||
private List<ProcessInfo> processList; // 存储所有进程信息
|
||||
private JTable table;
|
||||
|
||||
public ProcessSelectionWindow(Window owner) {
|
||||
super(owner, "选择进程", ModalityType.APPLICATION_MODAL);
|
||||
setSize(600, 400);
|
||||
setLocationRelativeTo(owner);
|
||||
initializeUI();
|
||||
}
|
||||
|
||||
private void initializeUI() {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BorderLayout());
|
||||
|
||||
// 获取所有进程信息
|
||||
processList = getProcesses();
|
||||
|
||||
// 创建搜索框
|
||||
JPanel searchPanel = new JPanel();
|
||||
searchPanel.setLayout(new BorderLayout());
|
||||
JTextField searchField = new JTextField();
|
||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
filterProcesses(searchField.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
filterProcesses(searchField.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
filterProcesses(searchField.getText());
|
||||
}
|
||||
});
|
||||
searchPanel.add(new JLabel("搜索进程: "), BorderLayout.WEST);
|
||||
searchPanel.add(searchField, BorderLayout.CENTER);
|
||||
panel.add(searchPanel, BorderLayout.NORTH);
|
||||
|
||||
// 显示进程信息的表格
|
||||
String[] columns = {"进程名称", "进程ID"};
|
||||
Object[][] data = getTableData(processList);
|
||||
table = new JTable(data, columns);
|
||||
JScrollPane scrollPane = new JScrollPane(table);
|
||||
panel.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
JButton selectButton = new JButton("选择");
|
||||
selectButton.addActionListener(e -> {
|
||||
int selectedRow = table.getSelectedRow();
|
||||
if (selectedRow >= 0) {
|
||||
long selectedPid = (Long) table.getValueAt(selectedRow, 1); // 注意这里获取的是PID列
|
||||
((FridaWindow) getOwner()).pidField.setText(String.valueOf(selectedPid));
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
panel.add(selectButton, BorderLayout.SOUTH);
|
||||
|
||||
setContentPane(panel);
|
||||
}
|
||||
|
||||
private void filterProcesses(String query) {
|
||||
List<ProcessInfo> filteredList = new ArrayList<>();
|
||||
for (ProcessInfo process : processList) {
|
||||
if (process.getName().toLowerCase().contains(query.toLowerCase())) {
|
||||
filteredList.add(process);
|
||||
}
|
||||
}
|
||||
// 更新表格数据
|
||||
Object[][] filteredData = getTableData(filteredList);
|
||||
table.setModel(new DefaultTableModel(filteredData, new String[]{"进程名称", "进程ID"}));
|
||||
}
|
||||
|
||||
private Object[][] getTableData(List<ProcessInfo> processes) {
|
||||
Object[][] data = new Object[processes.size()][2];
|
||||
for (int i = 0; i < processes.size(); i++) {
|
||||
ProcessInfo process = processes.get(i);
|
||||
data[i] = new Object[]{process.getName(), process.getPid()};
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private List<ProcessInfo> getProcesses() {
|
||||
List<ProcessInfo> processList = new ArrayList<>();
|
||||
try {
|
||||
// 执行cmd命令获取所有进程信息
|
||||
Process process = Runtime.getRuntime().exec("tasklist /fo csv /nh");
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
String[] columns = line.split(",");
|
||||
if (columns.length > 1) {
|
||||
long pid = Long.parseLong(columns[1].replaceAll("\"", "").trim());
|
||||
String processName = columns[0].replaceAll("\"", "").trim();
|
||||
processList.add(new ProcessInfo(pid, processName));
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return processList;
|
||||
}
|
||||
}
|
||||
|
||||
// 存储进程信息的类
|
||||
private static class ProcessInfo {
|
||||
private long pid;
|
||||
private String name;
|
||||
|
||||
public ProcessInfo(long pid, String name) {
|
||||
this.pid = pid;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/main/java/com/axis/innovators/box/gui/LoadIcon.java
Normal file
36
src/main/java/com/axis/innovators/box/gui/LoadIcon.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* 负责加载图片
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class LoadIcon {
|
||||
private static final String ICON_PATH = "/icons/";
|
||||
public static ImageIcon loadIcon(String filename, int size) {
|
||||
try {
|
||||
String fullPath = ICON_PATH + filename;
|
||||
URL imgUrl = LoadIcon.class.getResource(fullPath);
|
||||
if (imgUrl == null) {
|
||||
return createPlaceholderIcon(size);
|
||||
}
|
||||
Image image = new ImageIcon(imgUrl).getImage();
|
||||
return new ImageIcon(image.getScaledInstance(size, size, Image.SCALE_SMOOTH));
|
||||
} catch (Exception e) {
|
||||
return createPlaceholderIcon(size);
|
||||
}
|
||||
}
|
||||
|
||||
private static ImageIcon createPlaceholderIcon(int size) {
|
||||
BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = img.createGraphics();
|
||||
g2d.setColor(Color.LIGHT_GRAY);
|
||||
g2d.fillRect(0, 0, size, size);
|
||||
g2d.dispose();
|
||||
return new ImageIcon(img);
|
||||
}
|
||||
}
|
||||
716
src/main/java/com/axis/innovators/box/gui/MainWindow.java
Normal file
716
src/main/java/com/axis/innovators/box/gui/MainWindow.java
Normal file
@@ -0,0 +1,716 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
|
||||
import com.axis.innovators.box.events.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.plaf.PanelUI;
|
||||
import javax.swing.plaf.basic.BasicScrollBarUI;
|
||||
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ConvolveOp;
|
||||
import java.awt.image.Kernel;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 显示窗口(显示的主窗口)
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class MainWindow extends JFrame {
|
||||
private final Map<JComponent, Float> cardScales = new HashMap<>();
|
||||
private final Map<JComponent, Integer> cardElevations = new HashMap<>();
|
||||
// 选项卡颜色
|
||||
private final Color CARD_COLOR = Color.WHITE;
|
||||
private final List<ToolCategory> categories = new ArrayList<>();
|
||||
// 是否启用背景图片
|
||||
private final boolean isBackground = true;
|
||||
// 启动背景图片后是否启动背景模糊
|
||||
private final boolean isBlur = true;
|
||||
|
||||
public MainWindow() {
|
||||
setIconImage(LoadIcon.loadIcon("logo.png", 32).getImage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加工具分类
|
||||
* @param category 工具分类
|
||||
*/
|
||||
public void addToolCategory(ToolCategory category){
|
||||
categories.add(category);
|
||||
}
|
||||
|
||||
public void initUI() {
|
||||
setTitle("轴创工具箱 v1.0");
|
||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
setSize(1200, 800);
|
||||
setLocationRelativeTo(null);
|
||||
|
||||
JPanel mainPanel = new JPanel() {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
Color color1 = new Color(235, 241, 250);
|
||||
Color color2 = new Color(255, 255, 255);
|
||||
GradientPaint gp = new GradientPaint(0, 0, color1, getWidth(), getHeight(), color2);
|
||||
g2d.setPaint(gp);
|
||||
g2d.fillRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
if (isBackground) {
|
||||
try {
|
||||
ImageIcon backgroundImage = LoadIcon.loadIcon("start_page.png", 500);
|
||||
if (isBlur) {
|
||||
BufferedImage bufferedImage = toBufferedImage(backgroundImage.getImage());
|
||||
BufferedImage blurredImage = applyGaussianBlur(bufferedImage, 5);
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f));
|
||||
int x = (getWidth() - blurredImage.getWidth()) / 2;
|
||||
int y = (getHeight() - blurredImage.getHeight()) / 2;
|
||||
g2d.drawImage(blurredImage, x, y, this);
|
||||
} else {
|
||||
int x = (getWidth() - backgroundImage.getIconWidth()) / 2;
|
||||
int y = (getHeight() - backgroundImage.getIconHeight()) / 2;
|
||||
g2d.drawImage(backgroundImage.getImage(), x, y, this);
|
||||
}
|
||||
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mainPanel.setLayout(new BorderLayout(20, 20));
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30));
|
||||
|
||||
mainPanel.add(createHeader(), BorderLayout.NORTH);
|
||||
mainPanel.add(createCategoryTabs(), BorderLayout.CENTER);
|
||||
|
||||
GlobalEventBus.EVENT_BUS.post(new MainWindowEvents.initialize(this, mainPanel));
|
||||
|
||||
add(mainPanel);
|
||||
}
|
||||
|
||||
private BufferedImage toBufferedImage(Image img) {
|
||||
if (img instanceof BufferedImage) {
|
||||
return (BufferedImage) img;
|
||||
}
|
||||
BufferedImage bimage = new BufferedImage(
|
||||
img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D bGr = bimage.createGraphics();
|
||||
bGr.drawImage(img, 0, 0, null);
|
||||
bGr.dispose();
|
||||
return bimage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对图像应用动感模糊效果。
|
||||
*
|
||||
* @param srcImage 需要应用动感模糊的源图像。
|
||||
* 这个参数应该是一个 `BufferedImage` 类型的对象。
|
||||
* @param radius 动感模糊的半径,决定模糊效果的范围。
|
||||
* 半径越大,模糊范围越广,模糊效果越强。
|
||||
* @param angle 动感模糊的角度,决定模糊效果的方向。
|
||||
* 角度单位为度,表示从正Y轴顺时针的旋转角度。
|
||||
* 比如:
|
||||
* - `0` 表示水平方向的模糊。
|
||||
* - `90` 表示垂直方向的模糊。
|
||||
* - 其他角度则表示不同方向的模糊。
|
||||
* @return 返回应用了动感模糊效果的 `BufferedImage` 图像。
|
||||
* 原始图像不会被修改,返回的是处理过的新图像。
|
||||
*/
|
||||
public BufferedImage applyMotionBlur(BufferedImage srcImage, int radius, int angle) {
|
||||
double radian = Math.toRadians(angle);
|
||||
|
||||
float[] matrix = new float[radius * radius];
|
||||
float sum = 0.0f;
|
||||
int index = 0;
|
||||
|
||||
for (int y = -radius / 2; y <= radius / 2; y++) {
|
||||
for (int x = -radius / 2; x <= radius / 2; x++) {
|
||||
float weight = (float) (Math.cos(radian) * x + Math.sin(radian) * y);
|
||||
if (Math.abs(weight) < 1) {
|
||||
matrix[index++] = 1.0f;
|
||||
sum += 1.0f;
|
||||
} else {
|
||||
matrix[index++] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getBufferedImage(srcImage, radius, matrix, sum);
|
||||
}
|
||||
|
||||
private BufferedImage getBufferedImage(BufferedImage srcImage, int radius, float[] matrix, float sum) {
|
||||
for (int i = 0; i < matrix.length; i++) {
|
||||
matrix[i] /= sum;
|
||||
}
|
||||
Kernel kernel = new Kernel(radius, radius, matrix);
|
||||
ConvolveOp convolve = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
|
||||
return convolve.filter(srcImage, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用高斯模糊效果到给定的图像。
|
||||
*
|
||||
* @param srcImage 输入的原始图像,类型为 `BufferedImage`。
|
||||
* @param radius 高斯模糊的半径,表示模糊的强度。半径越大,模糊效果越强。
|
||||
* @return 返回模糊处理后的图像,类型为 `BufferedImage`。
|
||||
*/
|
||||
public BufferedImage applyGaussianBlur(BufferedImage srcImage, int radius) {
|
||||
if (radius > 100) {
|
||||
radius = 100;
|
||||
}
|
||||
|
||||
float[] matrix = new float[radius * radius];
|
||||
float sigma = radius / 2f;
|
||||
float sum = 0.0f;
|
||||
int index = 0;
|
||||
|
||||
for (int y = -radius / 2; y <= radius / 2; y++) {
|
||||
for (int x = -radius / 2; x <= radius / 2; x++) {
|
||||
float weight = (float) Math.exp(-(x * x + y * y) / (2 * sigma * sigma));
|
||||
matrix[index] = weight;
|
||||
index++;
|
||||
sum += weight;
|
||||
}
|
||||
}
|
||||
|
||||
return getBufferedImage(srcImage, radius, matrix, sum);
|
||||
}
|
||||
|
||||
private JComponent createCategoryTabs() {
|
||||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
|
||||
tabbedPane.setOpaque(false);
|
||||
tabbedPane.setBackground(new Color(0, 0, 0, 0));
|
||||
tabbedPane.setBorder(null);
|
||||
|
||||
tabbedPane.setUI(new CustomTabbedPaneUI());
|
||||
|
||||
for (ToolCategory category : categories) {
|
||||
JPanel toolsPanel = createToolsPanel(category);
|
||||
toolsPanel.setOpaque(false);
|
||||
toolsPanel.setBorder(null);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(toolsPanel);
|
||||
scrollPane.setBorder(null);
|
||||
scrollPane.setOpaque(false);
|
||||
scrollPane.getViewport().setOpaque(false);
|
||||
JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar();
|
||||
verticalScrollBar.setUI(new CustomScrollBarUI());
|
||||
verticalScrollBar.setPreferredSize(new Dimension(10, 100));
|
||||
|
||||
tabbedPane.addTab(
|
||||
category.getName(),
|
||||
LoadIcon.loadIcon(category.getIcon(), 24),
|
||||
scrollPane
|
||||
);
|
||||
}
|
||||
|
||||
return tabbedPane;
|
||||
}
|
||||
|
||||
private JPanel createToolsPanel(ToolCategory category) {
|
||||
JPanel panel = new JPanel(new GridLayout(0, 3, 20, 20));
|
||||
for (ToolItem tool : category.getTools()) {
|
||||
panel.add(createToolCard(tool));
|
||||
}
|
||||
panel.setOpaque(false);
|
||||
panel.setBorder(null);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createHeader() {
|
||||
JPanel header = new JPanel(new BorderLayout());
|
||||
header.setOpaque(false);
|
||||
header.setBorder(BorderFactory.createEmptyBorder(15, 30, 15, 30));
|
||||
|
||||
// 创建标题
|
||||
JLabel title = new JLabel("轴创工具箱");
|
||||
title.setFont(new Font("微软雅黑", Font.BOLD, 28));
|
||||
title.setForeground(new Color(44, 62, 80));
|
||||
|
||||
//JLabel iconLabel = new JLabel(LoadIcon.loadIcon("logo.png", 28));
|
||||
//iconLabel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
|
||||
|
||||
JButton settings = new JButton(LoadIcon.loadIcon("settings.png", 32));
|
||||
settings.setContentAreaFilled(false);
|
||||
settings.addActionListener(e -> showSettings());
|
||||
|
||||
JPanel titlePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
titlePanel.setOpaque(false);
|
||||
//titlePanel.add(iconLabel);
|
||||
titlePanel.add(title);
|
||||
|
||||
header.add(titlePanel, BorderLayout.WEST);
|
||||
header.add(settings, BorderLayout.EAST);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void update(Graphics g) {
|
||||
GlobalEventBus.EVENT_BUS.post(new MainWindowEvents.update(this, g));
|
||||
super.update(g);
|
||||
}
|
||||
|
||||
private void showSettings() {
|
||||
JDialog dialog = new JDialog(this, "系统设置", true);
|
||||
dialog.setSize(600, 400);
|
||||
dialog.setLocationRelativeTo(this);
|
||||
|
||||
JPanel content = new JPanel(new BorderLayout());
|
||||
content.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
|
||||
//JLabel placeholder = new JLabel("设置功能开发中...", SwingConstants.CENTER);
|
||||
//placeholder.setFont(new Font("微软雅黑", Font.PLAIN, 24));
|
||||
//placeholder.setForeground(new Color(127, 140, 153));
|
||||
//content.add(placeholder, BorderLayout.CENTER);
|
||||
|
||||
GlobalEventBus.EVENT_BUS.post(new SettingsLoadEvents(dialog, content));
|
||||
|
||||
dialog.add(content);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private JPanel createToolCard(ToolItem tool) {
|
||||
JPanel card = new JPanel() {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
Graphics2D g2d = (Graphics2D) g.create();
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// 绘制阴影
|
||||
int elevation = cardElevations.getOrDefault(this, 2);
|
||||
for (int i = 0; i < elevation; i++) {
|
||||
g2d.setColor(new Color(0, 0, 0, 20 - i * 2));
|
||||
g2d.fillRoundRect(i, i, getWidth() - i * 2, getHeight() - i * 2, 15, 15);
|
||||
}
|
||||
|
||||
float scale = cardScales.getOrDefault(this, 1.0f);
|
||||
int offset = (int) ((scale - 1) * getWidth() / 2);
|
||||
g2d.translate(-offset, -offset);
|
||||
g2d.scale(scale, scale);
|
||||
|
||||
super.paintComponent(g2d);
|
||||
g2d.dispose();
|
||||
}
|
||||
};
|
||||
|
||||
card.setLayout(new BorderLayout(15, 15));
|
||||
card.setBackground(CARD_COLOR);
|
||||
card.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(new Color(225, 229, 234), 1),
|
||||
BorderFactory.createEmptyBorder(20, 20, 20, 20)
|
||||
));
|
||||
card.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
|
||||
JLabel iconLabel = new JLabel(LoadIcon.loadIcon(tool.icon(), 64));
|
||||
iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
|
||||
// 文字面板
|
||||
JPanel textPanel = new JPanel();
|
||||
textPanel.setLayout(new BoxLayout(textPanel, BoxLayout.Y_AXIS));
|
||||
textPanel.setOpaque(false);
|
||||
|
||||
JLabel titleLabel = new JLabel(tool.title());
|
||||
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));
|
||||
titleLabel.setForeground(new Color(44, 62, 80));
|
||||
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
|
||||
JTextArea descArea = new JTextArea(tool.description());
|
||||
descArea.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
||||
descArea.setForeground(new Color(127, 140, 153));
|
||||
descArea.setLineWrap(true);
|
||||
descArea.setWrapStyleWord(true);
|
||||
descArea.setEditable(false);
|
||||
descArea.setOpaque(false);
|
||||
descArea.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
|
||||
card.setToolTipText(createToolTipHTML(tool));
|
||||
|
||||
textPanel.add(titleLabel);
|
||||
textPanel.add(Box.createVerticalStrut(10));
|
||||
textPanel.add(descArea);
|
||||
|
||||
card.add(iconLabel, BorderLayout.NORTH);
|
||||
card.add(textPanel, BorderLayout.CENTER);
|
||||
|
||||
// 鼠标悬停动画
|
||||
card.addMouseListener(new CardMouseAdapter(card, tool));
|
||||
card.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
card.setUI(new PanelUI() {
|
||||
@Override
|
||||
public void installUI(JComponent c) {
|
||||
GlobalEventBus.EVENT_BUS.post(new TABUIEvents(card, c));
|
||||
super.installUI(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Graphics g, JComponent c) {
|
||||
GlobalEventBus.EVENT_BUS.post(new TABUIEvents.update(g, c));
|
||||
super.update(g, c);
|
||||
}
|
||||
});
|
||||
return card;
|
||||
}
|
||||
|
||||
private BufferedImage captureWindowImage(Window window) {
|
||||
try {
|
||||
Rectangle bounds = window.getBounds();
|
||||
BufferedImage capture = new Robot().createScreenCapture(bounds);
|
||||
return capture.getSubimage(0, 0, window.getWidth(), window.getHeight());
|
||||
} catch (AWTException e) {
|
||||
e.printStackTrace();
|
||||
return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
}
|
||||
}
|
||||
|
||||
private static JPanel getjPanel(BufferedImage[] blurredImage, Window window) {
|
||||
JPanel blurPanel = new JPanel() {
|
||||
private float currentOpacity = 1.0f;
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
|
||||
if (blurredImage[0] != null) {
|
||||
Graphics2D g2d = (Graphics2D) g.create();
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, currentOpacity));
|
||||
g2d.drawImage(blurredImage[0], 0, 0, null);
|
||||
g2d.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 面板属性设置
|
||||
blurPanel.setBounds(0, 0, window.getWidth(), window.getHeight());
|
||||
blurPanel.setOpaque(false);
|
||||
return blurPanel;
|
||||
}
|
||||
|
||||
|
||||
private String createToolTipHTML(ToolItem tool) {
|
||||
return "<html><body style='width: 300px; padding: 10px;'>" +
|
||||
"<h3 style='color: #2c3e50; margin: 0 0 8px 0;'>" + tool.getName() + "</h3>" +
|
||||
"<p style='color: #7f8c99; margin: 0;'>" + tool.description() + "</p>" +
|
||||
"</body></html>";
|
||||
}
|
||||
|
||||
private void animateHover(JComponent component, float targetScale, int targetElevation) {
|
||||
final int ANIMATION_DURATION = 200;
|
||||
final float startScale = cardScales.getOrDefault(component, 1.0f);
|
||||
final int startElevation = cardElevations.getOrDefault(component, 2);
|
||||
|
||||
new Timer(10, new AbstractAction() {
|
||||
long startTime = -1;
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (startTime < 0) {
|
||||
startTime = System.currentTimeMillis();
|
||||
}
|
||||
long elapsed = System.currentTimeMillis() - startTime;
|
||||
float progress = Math.min(1.0f, elapsed / (float) ANIMATION_DURATION);
|
||||
|
||||
// 使用缓动函数实现平滑动画
|
||||
float easedProgress = (float) (1 - Math.pow(1 - progress, 3));
|
||||
|
||||
float currentScale = startScale + (targetScale - startScale) * easedProgress;
|
||||
int currentElevation = (int) (startElevation + (targetElevation - startElevation) * easedProgress);
|
||||
|
||||
cardScales.put(component, currentScale);
|
||||
cardElevations.put(component, currentElevation);
|
||||
|
||||
component.repaint();
|
||||
|
||||
if (progress >= 1.0f) {
|
||||
((Timer) e.getSource()).stop();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
// 工具类别内部类
|
||||
public static class ToolCategory {
|
||||
private final String name;
|
||||
private final String icon;
|
||||
private final String description;
|
||||
private final List<ToolItem> tools = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 一个大的分类项类
|
||||
* @param name 分类项的显示名称
|
||||
* @param icon 分类项的图标(resources的路径)
|
||||
* @param description 分类项的描述
|
||||
*/
|
||||
public ToolCategory(String name, String icon, String description) {
|
||||
this.name = name;
|
||||
this.icon = icon;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册工具的方法
|
||||
* @param tool 工具项
|
||||
*/
|
||||
public void addTool(ToolItem tool) {
|
||||
tools.add(tool);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<ToolItem> getTools() {
|
||||
return tools;
|
||||
}
|
||||
}
|
||||
|
||||
// 工具项数据类
|
||||
|
||||
/**
|
||||
* 工具注册类
|
||||
* @param title 工具的标题(显示名称)
|
||||
* @param icon 工具的图标(resources的路径)
|
||||
* @param description 工具的描述
|
||||
* @param id 工具的id(请不要重复注册相同id的工具)
|
||||
* @param action 工具的点击事件
|
||||
*/
|
||||
public record ToolItem(String title, String icon, String description, int id, Action action) {
|
||||
private static JPanel getjPanel() {
|
||||
JPanel content = new JPanel() {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// 渐变背景
|
||||
GradientPaint gp = new GradientPaint(
|
||||
0, 0, new Color(245, 247, 250),
|
||||
getWidth(), getHeight(), new Color(255, 255, 255)
|
||||
);
|
||||
g2d.setPaint(gp);
|
||||
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 20, 20);
|
||||
|
||||
// 边框阴影
|
||||
g2d.setColor(new Color(0, 0, 0, 20));
|
||||
g2d.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 20, 20);
|
||||
}
|
||||
};
|
||||
content.setLayout(new BorderLayout(20, 20));
|
||||
content.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
return content;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
// 分类栏面卡
|
||||
public static class CustomTabbedPaneUI extends BasicTabbedPaneUI {
|
||||
private static final Color SELECTED_COLOR = new Color(183, 202, 221);
|
||||
private static final Color UNSELECTED_COLOR = new Color(125, 174, 237);
|
||||
|
||||
|
||||
@Override
|
||||
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);
|
||||
|
||||
if (isSelected) {
|
||||
g2d.setColor(SELECTED_COLOR);
|
||||
} else {
|
||||
g2d.setColor(UNSELECTED_COLOR);
|
||||
}
|
||||
|
||||
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 {
|
||||
private final JPanel card;
|
||||
private final ToolItem tool;
|
||||
private Timer pressTimer;
|
||||
private Timer releaseTimer;
|
||||
|
||||
public CardMouseAdapter(JPanel card, ToolItem tool) {
|
||||
this.card = card;
|
||||
this.tool = tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
startPressAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
startReleaseAnimation(() -> tool.action().actionPerformed(
|
||||
new ActionEvent(card, ActionEvent.ACTION_PERFORMED, "")
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
if (pressTimer != null && pressTimer.isRunning()) {
|
||||
startReleaseAnimation(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void startPressAnimation() {
|
||||
if (pressTimer != null && pressTimer.isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pressTimer = new Timer(10, new AbstractAction() {
|
||||
private final long startTime = System.currentTimeMillis();
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
float progress = Math.min(1.0f,
|
||||
(System.currentTimeMillis() - startTime) / 150f);
|
||||
|
||||
// 使用二次缓动函数
|
||||
float scale = 1.0f - 0.1f * (float) Math.pow(progress, 0.5);
|
||||
cardScales.put(card, scale);
|
||||
card.repaint();
|
||||
|
||||
if (progress >= 1.0f) {
|
||||
((Timer) e.getSource()).stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
pressTimer.start();
|
||||
}
|
||||
|
||||
private void startReleaseAnimation(Runnable callback) {
|
||||
if (pressTimer != null) {
|
||||
pressTimer.stop();
|
||||
}
|
||||
if (releaseTimer != null && releaseTimer.isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final float startScale = cardScales.getOrDefault(card, 1.0f);
|
||||
releaseTimer = new Timer(10, new AbstractAction() {
|
||||
private final long startTime = System.currentTimeMillis();
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
float progress = Math.min(1.0f,
|
||||
(System.currentTimeMillis() - startTime) / 200f);
|
||||
|
||||
// 使用弹性缓动函数
|
||||
float scale = startScale +
|
||||
(1.0f - startScale) * (float) (1 - Math.pow(1 - progress, 3));
|
||||
cardScales.put(card, scale);
|
||||
card.repaint();
|
||||
|
||||
if (progress >= 1.0f) {
|
||||
((Timer) e.getSource()).stop();
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
releaseTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static class CustomScrollBarUI extends BasicScrollBarUI {
|
||||
@Override
|
||||
protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) {
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// 设置轨道背景颜色
|
||||
Color trackColor = new Color(240, 240, 240);
|
||||
g2d.setColor(trackColor);
|
||||
g2d.fillRoundRect(
|
||||
trackBounds.x,
|
||||
trackBounds.y,
|
||||
trackBounds.width - 1,
|
||||
trackBounds.height - 1,
|
||||
5, 5 // 圆角半径
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
Color thumbColor = new Color(180, 180, 180);
|
||||
g2d.setColor(thumbColor);
|
||||
g2d.fillRoundRect(
|
||||
thumbBounds.x,
|
||||
thumbBounds.y,
|
||||
thumbBounds.width - 1,
|
||||
thumbBounds.height - 1,
|
||||
5, 5
|
||||
);
|
||||
|
||||
Color borderColor = new Color(160, 160, 160);
|
||||
g2d.setColor(borderColor);
|
||||
g2d.drawRoundRect(
|
||||
thumbBounds.x,
|
||||
thumbBounds.y,
|
||||
thumbBounds.width - 1,
|
||||
thumbBounds.height - 1,
|
||||
5, 5
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintDecreaseHighlight(Graphics g) {}
|
||||
|
||||
@Override
|
||||
protected void paintIncreaseHighlight(Graphics g) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.axis.innovators.box.tools;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 在当前jar下创建文件夹
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class FolderCreator {
|
||||
|
||||
public static final String LIBRARY_NAME = "library";
|
||||
|
||||
public static String getLibraryFolder() {
|
||||
String folder = createFolder(LIBRARY_NAME);
|
||||
if (folder == null) {
|
||||
System.out.println("Library folder creation failure");
|
||||
return null;
|
||||
}
|
||||
return folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件夹
|
||||
* @param folderName folder name
|
||||
* @return folder path
|
||||
*/
|
||||
private static String createFolder(String folderName) {
|
||||
String jarDir = System.getProperty("user.dir");
|
||||
File folder = new File(jarDir, folderName);
|
||||
if (!folder.exists()) {
|
||||
if (!folder.mkdir()) {
|
||||
System.out.println("Folder creation failure");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return folder.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
40
src/main/java/com/axis/innovators/box/tools/LibraryLoad.java
Normal file
40
src/main/java/com/axis/innovators/box/tools/LibraryLoad.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.axis.innovators.box.tools;
|
||||
|
||||
/**
|
||||
* 在程序链接库文件中加载指定链接库
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class LibraryLoad {
|
||||
public static final String LIBRARY_NAME = FolderCreator.getLibraryFolder() + "\\";
|
||||
|
||||
/**
|
||||
* 加载链接库
|
||||
* @param libraryName 链接库名称
|
||||
*/
|
||||
public static void loadLibrary(String libraryName) {
|
||||
String libraryWithExtension = addLibraryExtension(libraryName);
|
||||
System.load(LIBRARY_NAME + libraryWithExtension);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断并添加正确的库文件后缀
|
||||
* @param libraryName 链接库名称
|
||||
* @return 带有后缀名的库文件名称
|
||||
*/
|
||||
private static String addLibraryExtension(String libraryName) {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
if (libraryName.toLowerCase().endsWith(".dll")
|
||||
|| libraryName.toLowerCase().endsWith(".so")) {
|
||||
return libraryName;
|
||||
}
|
||||
|
||||
if (os.contains("win")) {
|
||||
return libraryName + ".dll";
|
||||
} else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) {
|
||||
return libraryName + ".so";
|
||||
} else {
|
||||
return libraryName;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user