feat(gui): 添加系统托盘功能并优化 UI

- 实现系统托盘支持,包含打开主界面、反编译工具和退出程序的菜单项
- 添加窗口关闭时隐藏而不是退出的功能
-优化托盘图标,使用圆角图像
- 增加中文字体支持和全局字体设置- 移除 AIChatDialog 中的 MathJax 配置- 更新 BoxClassLoader,添加 com.dustinredmond 包的访问权限
- 在 build.gradle 中添加 SystemTray4J 和 JavaFX相关依赖
This commit is contained in:
tzdwindows 7
2025-03-16 12:17:00 +08:00
parent 6b0a81175e
commit 9da8953253

View File

@@ -0,0 +1,251 @@
package com.axis.innovators.box.gui;
import com.axis.innovators.box.AxisInnovatorsBox;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.RoundRectangle2D;
/**
* 创建一个具有圆角效果的系统托盘图标,并添加右键菜单。
* (未完成)
* @author tzdwindows 7
*/
public class ModernTrayApp {
private static final int ITEM_WIDTH = 145;
private static final int ITEM_HEIGHT = 29;
private static final int ICON_SIZE = 16;
private static final int WINDOW_CORNER = 6;
private static final Color MENU_BG_COLOR = new Color(250, 250, 250);
private TrayIcon trayIcon;
private JPopupMenu popupMenu;
private JWindow mainWindow;
public static void main(String[] args) {
if (!System.getProperty("os.name").contains("Windows")) {
JOptionPane.showMessageDialog(null, "本程序仅支持Windows系统");
return;
}
new ModernTrayApp().initTray();
}
private void initTray() {
try {
// 1. 加载并缩放托盘图标
Image trayImage = LoadIcon.loadIcon("logo.png", 32).getImage();
trayIcon = new TrayIcon(trayImage, "轴创工具箱");
trayIcon.setImageAutoSize(true);
// 2. 创建主窗口
createMainWindow();
// 3. 创建圆角菜单
createRoundPopupMenu();
// 4. 配置系统托盘
SystemTray.getSystemTray().add(trayIcon);
trayIcon.addMouseListener(new TrayMouseListener());
} catch (Exception e) {
e.printStackTrace();
}
}
private void createMainWindow() {
mainWindow = new JWindow() {
@Override
public void paint(Graphics g) {
// 创建临时图形上下文
Graphics2D g2 = (Graphics2D) g.create();
// 启用高质量渲染
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
// 绘制透明背景(关键步骤)
g2.setComposite(AlphaComposite.Clear);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.setComposite(AlphaComposite.SrcOver);
// 绘制圆角面板
g2.setColor(MENU_BG_COLOR);
g2.fillRoundRect(0, 0, getWidth(), getHeight(), WINDOW_CORNER, WINDOW_CORNER);
// 绘制边框
g2.setColor(new Color(220, 220, 220));
g2.drawRoundRect(0, 0, getWidth()-1, getHeight()-1, WINDOW_CORNER, WINDOW_CORNER);
g2.dispose();
}
};
mainWindow.setSize(ITEM_WIDTH, ITEM_HEIGHT * 3 + 10);
mainWindow.setShape(new RoundRectangle2D.Float(0, 0,
mainWindow.getWidth(), mainWindow.getHeight(), WINDOW_CORNER, WINDOW_CORNER));
// 合并后的焦点监听器
mainWindow.addWindowFocusListener(new WindowAdapter() {
@Override
public void windowLostFocus(WindowEvent e) {
closeMenuAndWindow();
}
});
mainWindow.setType(Window.Type.POPUP);
}
private void closeMenuAndWindow() {
SwingUtilities.invokeLater(() -> {
// 添加淡出动画
new Thread(() -> {
for (float opacity = 1.0f; opacity > 0; opacity -= 0.1f) {
final float finalOpacity = opacity;
SwingUtilities.invokeLater(() -> {
mainWindow.setOpacity(finalOpacity);
});
try { Thread.sleep(20); } catch (InterruptedException e) {}
}
SwingUtilities.invokeLater(() -> {
popupMenu.setVisible(false);
mainWindow.setVisible(false);
mainWindow.setOpacity(1.0f); // 重置透明度
});
}).start();
});
}
private void updateWindowShape() {
RoundRectangle2D shape = new RoundRectangle2D.Float(
0, 0,
mainWindow.getWidth(), mainWindow.getHeight(),
WINDOW_CORNER, WINDOW_CORNER
);
mainWindow.setShape(shape);
}
private void updateWindowSize(int width, int height) {
mainWindow.setSize(width, height);
updateWindowShape();
}
private void createRoundPopupMenu() {
popupMenu = new JPopupMenu(){
@Override
protected void firePopupMenuCanceled() {
closeMenuAndWindow();
super.firePopupMenuWillBecomeInvisible();
}
};
popupMenu.setLayout(new GridLayout(0, 1, 0, 1));
JPanel customSeparator = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(220, 220, 220));
g.fillRect(5, getHeight()/2, getWidth()-10, 1);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(ITEM_WIDTH, 4);
}
};
customSeparator.setOpaque(false);
popupMenu.add(ComponentFactory.createTextComponent("AxisInnovatorsBox",
new Color(0, 0, 0),
15,
new Dimension(ITEM_WIDTH, 4)));
popupMenu.add(ComponentFactory.createTextComponent(" ————"
+ AxisInnovatorsBox.getVersion(),
new Color(83, 83, 83),
12,
new Dimension(ITEM_WIDTH, 4)));
popupMenu.add(customSeparator);
addStyledMenuItem("打开主界面", "home.png");
addStyledMenuItem("Java反编译", "decompile.png");
popupMenu.add(customSeparator);
addStyledMenuItem("退出程序", "exit.png");
}
private void addStyledMenuItem(String text, String iconPath) {
JMenuItem item = new JMenuItem(text) {
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 绘制背景
if (isArmed()) {
g2.setColor(new Color(161, 161, 161));
g2.fillRoundRect(0, 0, getWidth(), getHeight(), 4, 4);
}
// 绘制图标
Image icon = LoadIcon.loadIcon(iconPath, ICON_SIZE).getImage();
g2.drawImage(icon, 8, (ITEM_HEIGHT - ICON_SIZE)/2, null);
// 绘制文本
g2.setFont(new Font("微软雅黑", Font.PLAIN, 12));
g2.setColor(Color.DARK_GRAY);
g2.drawString(text, 28, (ITEM_HEIGHT + g2.getFontMetrics().getAscent())/2 - 1);
g2.dispose();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(ITEM_WIDTH, ITEM_HEIGHT);
}
};
item.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8));
item.setOpaque(false);
popupMenu.add(item);
}
private class TrayMouseListener extends MouseAdapter {
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
showMenu(e.getX(), e.getY());
}
}
private void showMenu(int x, int y) {
Point mousePos = MouseInfo.getPointerInfo().getLocation();
Point menuLocation = calculateMenuPosition(mousePos);
mainWindow.setLocation(menuLocation);
mainWindow.setVisible(true);
popupMenu.show(mainWindow.getContentPane(), 0, 0);
}
private Point calculateMenuPosition(Point pos) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int menuHeight = mainWindow.getHeight();
// 默认显示在鼠标右上方
int finalX = pos.x + 16; // 向右偏移16px
int finalY = pos.y - menuHeight - 8; // 向上偏移8px
// 右侧边界检测
if (finalX + ITEM_WIDTH > screenSize.width) {
finalX = screenSize.width - ITEM_WIDTH - 8;
}
// 顶部溢出检测
if (finalY < 0) {
finalY = pos.y + 16; // 改为显示在下方
}
return new Point(finalX, finalY);
}
}
}