feat(decryption):重构QQ音乐解密工具并增强播放功能
- 新增音频播放功能,支持mp3/ogg/flac格式 - 实现可视化频谱显示与粒子效果- 添加播放列表管理与文件拖放支持 - 改进UI设计,使用现代化布局与配色方案 - 增加设置对话框,支持自定义输出路径 - 实现播放控制(播放/暂停/停止)与进度条拖动- 添加文件信息查看与资源管理器定位功能 -优化日志显示与错误处理机制 - 支持快捷键操作(空格切换播放/暂停)- 增强文件列表渲染,支持长文件名换行显示
This commit is contained in:
@@ -115,6 +115,14 @@ dependencies {
|
||||
implementation 'com.github.axet:TarsosDSP:2.4'
|
||||
implementation 'org.json:json:20231013'
|
||||
implementation 'org.casbin:casdoor-java-sdk:1.37.0'
|
||||
|
||||
//implementation 'com.googlecode.soundlibs:tritonus-share:0.3.6-2'
|
||||
implementation 'com.googlecode.soundlibs:mp3spi:1.9.5-1' // mp3 支持
|
||||
implementation 'com.googlecode.soundlibs:vorbisspi:1.0.3-2' // ogg 支持
|
||||
//implementation 'com.googlecode.soundlibs:flac:1.3.3-1' // flac 支持(示例)
|
||||
implementation 'com.googlecode.soundlibs:jorbis:0.0.17-2' // ogg 依赖
|
||||
|
||||
implementation 'cn.dev33:sa-token-spring-boot-starter:1.44.0'
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#Current Loaded Language
|
||||
#Mon Aug 18 02:11:52 CST 2025
|
||||
#Sun Oct 05 16:06:50 CST 2025
|
||||
loadedLanguage=system\:zh_CN
|
||||
|
||||
@@ -41,6 +41,9 @@ material_oceanic_theme.default.tip=\u57FA\u4E8E MaterialLookAndFeel \u7684\u6D45
|
||||
flatLightLaf_theme.system.topicName=flatLightLaf\u98CE\u683C
|
||||
flatLightLaf_theme.default.tip=flatLightLaf\u98CE\u683C
|
||||
|
||||
blur.system.topicName=\u6A21\u7CCA\u98CE\u683C
|
||||
blur.default.tip=\u6A21\u7CCA\u4E3B\u9898\uFF0C\u9002\u7528\u4E8E\u9AD8\u5BF9\u6BD4\u5EA6\u7684\u4E3B\u9898
|
||||
|
||||
mainWindow.title=\u8F74\u521B\u5DE5\u5177\u7BB1 v1.0
|
||||
mainWindow.title.2=\u8F74\u521B\u5DE5\u5177\u7BB1
|
||||
mainWindow.settings.title=\u7CFB\u7EDF\u8BBE\u7F6E
|
||||
@@ -91,7 +94,7 @@ fridaWindow.settings.font=\u5B57\u4F53
|
||||
fridaWindow.settings.size=\u5927\u5C0F
|
||||
fridaWindow.settings.theme=\u4E3B\u9898
|
||||
fridaWindow.menu.settingsMenu.settingsItem.1=\u8BBE\u7F6E
|
||||
fridaWindow.menu.help.about=\u5173\u4E8E
|
||||
fridaWindow.menu.help.about=\u5173\u4E8E\u6211\u4EEC
|
||||
|
||||
localWindow.newBtn=\u65B0\u5BF9\u8BDD
|
||||
localWindow.saveBtn=\u4FDD\u5B58\u8BB0\u5F55
|
||||
@@ -165,10 +168,10 @@ fridaWindow.font.preview=\u793A\u4F8B\u6587\u672C\uFF1Aconsole.log("Hello Frida"
|
||||
fridaWindow.about.title=\u5173\u4E8E
|
||||
fridaWindow.about.content=\u57FA\u4E8EFrida\u7684\u73B0\u4EE3\u6CE8\u5165\u5DE5\u5177\n\n\u7248\u6743\u6240\u6709 \u00A9 {0}
|
||||
|
||||
settings.1.title=\u63D2\u4EF6
|
||||
settings.1.title=\u63D2\u4EF6\u7BA1\u7406
|
||||
settings.2.title=\u57FA\u7840\u8BBE\u7F6E
|
||||
settings.3.title=\u5173\u4E8E
|
||||
settings.4.title=\u4E3B\u9898
|
||||
settings.3.title=\u5173\u4E8E\u6211\u4EEC
|
||||
settings.4.title=\u4E3B\u9898\u8BBE\u7F6E
|
||||
settings.1.tip=\u63D2\u4EF6\u7BA1\u7406
|
||||
settings.2.tip=\u5916\u89C2\u8BBE\u7F6E
|
||||
settings.3.tip=\u7248\u672C\u4FE1\u606F
|
||||
@@ -194,6 +197,17 @@ settings.2.language=\u754C\u9762\u8BED\u8A00\uFF1A
|
||||
settings.2.language.error=\u672A\u77E5\u8BED\u8A00
|
||||
settings.3.infoArea.1=\u8F6F\u4EF6\u7248\u672C:
|
||||
settings.3.infoArea.2=\u5F00\u53D1\u4F5C\u8005:
|
||||
settings.3.infoArea.description=\u8FD9\u662F\u4E00\u4E2A\u529F\u80FD\u5F3A\u5927\u7684\u521B\u65B0\u5DE5\u5177\u7BB1\uFF0C\u96C6\u6210\u4E86\u591A\u79CD\u5B9E\u7528\u5DE5\u5177\u3002
|
||||
settings.3.infoArea.features=\u4E3B\u8981\u529F\u80FD
|
||||
settings.3.infoArea.feature1=\u6570\u636E\u5206\u6790\u548C\u53EF\u89C6\u5316
|
||||
settings.3.infoArea.feature2=\u81EA\u52A8\u5316\u5904\u7406
|
||||
settings.3.infoArea.feature3=\u81EA\u5B9A\u4E49\u63D2\u4EF6\u652F\u6301
|
||||
settings.3.infoArea.email=tzdwindows 7
|
||||
settings.3.infoArea.website=https://axisInnovatorsBox.com
|
||||
settings.3.infoArea.copyright=\u7248\u6743\u6240\u6709 \u00A9 2025 Axis Innovators. \u4FDD\u7559\u6240\u6709\u6743\u5229\u3002
|
||||
settings.3.infoArea.requirement1=Windows 10 \u6216\u66F4\u9AD8\u7248\u672C
|
||||
settings.3.infoArea.requirement2=\u81F3\u5C11 4GB \u5185\u5B58
|
||||
settings.3.infoArea.support=\u5982\u6709\u95EE\u9898\u8BF7\u8054\u7CFB\u6280\u672F\u652F\u6301\u56E2\u961F
|
||||
settings.4.no_theme=\u6CA1\u6709\u53EF\u7528\u7684\u4E3B\u9898
|
||||
settings.4.search=\u641C\u7D22
|
||||
settings.4.search_empty=\u8BF7\u8F93\u5165\u641C\u7D22\u5185\u5BB9\uFF01
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -21,6 +21,8 @@ import com.axis.innovators.box.verification.CasdoorServer;
|
||||
import com.axis.innovators.box.verification.LoginData;
|
||||
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
|
||||
import com.formdev.flatlaf.themes.FlatMacLightLaf;
|
||||
import com.jogamp.nativewindow.swt.SWTAccessor;
|
||||
import com.sun.javafx.stage.WindowHelper;
|
||||
import com.sun.management.HotSpotDiagnosticMXBean;
|
||||
import mdlaf.MaterialLookAndFeel;
|
||||
import mdlaf.themes.JMarsDarkTheme;
|
||||
@@ -65,6 +67,7 @@ public class AxisInnovatorsBox {
|
||||
private static final String[] AUTHOR = new String[]{
|
||||
"tzdwindows 7",
|
||||
"lyxyz5223",
|
||||
"\uD83D\uDC3EMr. Liu\uD83D\uDC3E",
|
||||
};
|
||||
|
||||
/** 我是总任务数 **/
|
||||
@@ -75,6 +78,7 @@ public class AxisInnovatorsBox {
|
||||
LanguageManager.getLoadedLanguages().getText("progressBarManager.title"),
|
||||
totalTasks);
|
||||
private static AxisInnovatorsBox main;
|
||||
private final boolean quickStart;
|
||||
private MainWindow ex;
|
||||
private Thread thread;
|
||||
private final String[] args;
|
||||
@@ -88,19 +92,33 @@ public class AxisInnovatorsBox {
|
||||
|
||||
private static LoginData loginData;
|
||||
|
||||
public AxisInnovatorsBox(String[] args, boolean isDebug) {
|
||||
public AxisInnovatorsBox(String[] args, boolean isDebug, boolean quickStart) {
|
||||
this.args = args;
|
||||
this.isDebug = isDebug;
|
||||
this.quickStart = quickStart;
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
||||
organizingCrashReports(throwable instanceof Exception ?
|
||||
(Exception) throwable : new Exception(throwable));
|
||||
});
|
||||
|
||||
if (quickStart){
|
||||
initLog4j2();
|
||||
setTopic();
|
||||
main = this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前环境是否为快速启动环境
|
||||
*/
|
||||
public boolean getQuickStart() {
|
||||
return quickStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出登录窗口
|
||||
*/
|
||||
private void popupLogin(){
|
||||
private void popupLogin() {
|
||||
// 加载登录信息,如果没有,弹出登录弹窗,后续可以删掉默认弹出
|
||||
// TODO: login window should not be show when AxisInnovatorsBox initialize,
|
||||
// it should be show when user click login button.
|
||||
@@ -905,6 +923,14 @@ public class AxisInnovatorsBox {
|
||||
true
|
||||
);
|
||||
|
||||
//main.registrationTopic.addTopic(
|
||||
// new BlurTopic(),
|
||||
// LanguageManager.getLoadedLanguages().getText("blur.system.topicName"),
|
||||
// LanguageManager.getLoadedLanguages().getText("blur.default.tip"),
|
||||
// LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
// "system:blur",true
|
||||
//);
|
||||
|
||||
LookAndFeel defaultLaf = isDarkMode ? new FlatMacDarkLaf() : new FlatMacLightLaf();
|
||||
UIManager.setLookAndFeel(defaultLaf);
|
||||
main.registrationTopic.setLoading(
|
||||
@@ -991,40 +1017,41 @@ public class AxisInnovatorsBox {
|
||||
debugWindow.setVisible(true);
|
||||
}
|
||||
|
||||
public static void run(String[] args, boolean isDebug) {
|
||||
public static void run(String[] args, boolean isDebug, boolean quickStart) {
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
||||
if (main != null) {
|
||||
main.organizingCrashReports(throwable instanceof Exception ?
|
||||
(Exception) throwable : new Exception(throwable));
|
||||
} else {
|
||||
new AxisInnovatorsBox(args, isDebug)
|
||||
new AxisInnovatorsBox(args, isDebug,true)
|
||||
.organizingCrashReports(throwable instanceof Exception ?
|
||||
(Exception) throwable : new Exception(throwable));
|
||||
}
|
||||
});
|
||||
|
||||
// 设置EDT(事件调度线程)的异常处理器
|
||||
// Set the exception handler for EDT(event dispatcher thread)
|
||||
System.setProperty("sun.awt.exception.handler", EDTCrashHandler.class.getName());
|
||||
|
||||
main = new AxisInnovatorsBox(args,isDebug);
|
||||
// Check if AxisInnovatorsBox is started
|
||||
// If it's started, and it's not a quick start, don't allow it
|
||||
// because it's already started
|
||||
// Stop loading if the current running context is the quickStart context
|
||||
if (AxisInnovatorsBox.getMain() != null
|
||||
&& !AxisInnovatorsBox.getMain().getQuickStart() || quickStart) {
|
||||
// Manually created if it is a quickStart context and the AxisInnovatorsBox instance in the context is empty
|
||||
if (AxisInnovatorsBox.getMain() == null && quickStart) {
|
||||
new AxisInnovatorsBox(args,isDebug,true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
main = new AxisInnovatorsBox(args,isDebug,false);
|
||||
try {
|
||||
main.initLog4j2();
|
||||
main.setTopic();
|
||||
|
||||
main.popupLogin();
|
||||
List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
|
||||
for (Map<String, String> fileInfo : validFiles) {
|
||||
String extension = fileInfo.get("extension");
|
||||
String path = fileInfo.get("path");
|
||||
OpenFileEvents openFileEvents = new OpenFileEvents(path, extension);
|
||||
GlobalEventBus.EVENT_BUS.post(openFileEvents);
|
||||
if (!openFileEvents.isContinue()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//main.popupLogin();
|
||||
main.thread = new Thread(() -> {
|
||||
|
||||
try {
|
||||
// 主任务1:加载插件
|
||||
logger.info("Loaded plugins Started");
|
||||
@@ -1033,6 +1060,17 @@ public class AxisInnovatorsBox {
|
||||
PluginPyLoader.loadAllPlugins();
|
||||
logger.info("Loaded plugins End");
|
||||
|
||||
List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
|
||||
for (Map<String, String> fileInfo : validFiles) {
|
||||
String extension = fileInfo.get("extension");
|
||||
String path = fileInfo.get("path");
|
||||
OpenFileEvents openFileEvents = new OpenFileEvents(path, extension);
|
||||
GlobalEventBus.EVENT_BUS.post(openFileEvents);
|
||||
if (!openFileEvents.isContinue()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
main.progressBarManager.close();
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
@@ -1111,6 +1149,7 @@ public class AxisInnovatorsBox {
|
||||
}
|
||||
|
||||
ex.initUI();
|
||||
RegistrationSettingsItem.applyAllSettings();
|
||||
isWindow = true;
|
||||
ex.setVisible(true);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.axis.innovators.box.register.LanguageManager;
|
||||
import com.axis.innovators.box.tools.ArgsParser;
|
||||
import com.axis.innovators.box.tools.FolderCleaner;
|
||||
import com.axis.innovators.box.tools.FolderCreator;
|
||||
import org.QQdecryption.ui.DecryptionUI;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
@@ -14,10 +15,7 @@ import java.io.RandomAccessFile;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.channels.OverlappingFileLockException;
|
||||
import java.text.Format;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author tzdwindows 7
|
||||
@@ -30,53 +28,46 @@ public class Main {
|
||||
private static FileChannel lockChannel = null;
|
||||
private final static boolean releaseEnvironments = false;
|
||||
public static void main(String[] args) {
|
||||
// 清理日志文件(最大日志为10)
|
||||
FolderCleaner.cleanFolder(FolderCreator.getLogsFolder(), 10);
|
||||
|
||||
// 加载保存的语言
|
||||
LanguageManager.loadSavedLanguage();
|
||||
// 如果没有加载的语言,则加载默认的语言
|
||||
if (LanguageManager.getLoadedLanguages() == null) {
|
||||
LanguageManager.loadLanguage("system:zh_CN");
|
||||
}
|
||||
|
||||
// 检查是否包含调试控制台参数
|
||||
boolean debugWindowEnabled = false;
|
||||
String pluginsDirectory = null;
|
||||
|
||||
// 新增:预处理参数,移除已处理的参数
|
||||
List<String> remainingArgs = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (!releaseEnvironments && "-debugControlWindow-on".equals(args[i])) {
|
||||
for (String arg : args) {
|
||||
if (!releaseEnvironments && "-debugControlWindow-on".equals(arg)) {
|
||||
debugWindowEnabled = true;
|
||||
// 不添加到剩余参数中
|
||||
continue;
|
||||
}
|
||||
|
||||
// 新增:解析pluginsDirectory参数
|
||||
if (args[i].startsWith("pluginsDirectory=")) {
|
||||
pluginsDirectory = args[i].substring("pluginsDirectory=".length());
|
||||
// 移除引号(如果存在)
|
||||
if (arg.startsWith("pluginsDirectory=")) {
|
||||
pluginsDirectory = arg.substring("pluginsDirectory=".length());
|
||||
if (pluginsDirectory.startsWith("\"") && pluginsDirectory.endsWith("\"")) {
|
||||
pluginsDirectory = pluginsDirectory.substring(1, pluginsDirectory.length() - 1);
|
||||
}
|
||||
// 不添加到剩余参数中
|
||||
continue;
|
||||
}
|
||||
|
||||
// 其他参数保留
|
||||
remainingArgs.add(args[i]);
|
||||
remainingArgs.add(arg);
|
||||
}
|
||||
|
||||
// 如果有设置插件目录,在这里进行设置
|
||||
if (pluginsDirectory != null && !pluginsDirectory.isEmpty()) {
|
||||
System.out.println("Setting up the plugin directory: " + pluginsDirectory);
|
||||
FolderCreator.setPluginPath(pluginsDirectory);
|
||||
}
|
||||
|
||||
boolean quickStart = false;
|
||||
|
||||
String[] processedArgs = remainingArgs.toArray(new String[0]);
|
||||
|
||||
final Set<String> mUSICEXTS = new HashSet<>(Arrays.asList(
|
||||
".mflac", ".mgg", ".qmc0", ".qmc3", ".qmcflac", ".qmcogg",
|
||||
".tkm", ".qmc2", ".bkcmp3", ".bkcflac", ".ogg"
|
||||
));
|
||||
List<Map<String, String>> validFiles = ArgsParser.parseArgs(processedArgs);
|
||||
for (Map<String, String> fileInfo : validFiles) {
|
||||
String extension = fileInfo.get("extension");
|
||||
@@ -85,27 +76,35 @@ public class Main {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
UIManager.setLookAndFeel(new com.formdev.flatlaf.FlatDarculaLaf());
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
ModernJarViewer viewer = new ModernJarViewer(null, path);
|
||||
viewer.setVisible(true);
|
||||
});
|
||||
releaseLock(); // 释放锁(窗口模式)
|
||||
return;
|
||||
quickStart = true;
|
||||
}
|
||||
|
||||
if (".html".equals(extension)) {
|
||||
MainApplication.popupHTMLWindow(path);
|
||||
releaseLock();
|
||||
return;
|
||||
quickStart = true;
|
||||
}
|
||||
|
||||
if (extension != null && mUSICEXTS.contains(extension.toLowerCase(Locale.ROOT))) {
|
||||
final String p = path;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
DecryptionUI ui = new DecryptionUI(p);
|
||||
ui.setVisible(true);
|
||||
});
|
||||
releaseLock();
|
||||
quickStart = true;
|
||||
}
|
||||
}
|
||||
if (!acquireLock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AxisInnovatorsBox.run(processedArgs, debugWindowEnabled);
|
||||
AxisInnovatorsBox.run(processedArgs, debugWindowEnabled,quickStart);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.axis.innovators.box.login;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class LoginStpUtil {
|
||||
public static void login(Object id){
|
||||
StpUtil.login(id);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
LoginStpUtil.login(1);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,14 +14,18 @@ import javax.swing.border.Border;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.plaf.FontUIResource;
|
||||
import javax.swing.plaf.LayerUI;
|
||||
import javax.swing.plaf.PanelUI;
|
||||
import javax.swing.plaf.basic.BasicScrollBarUI;
|
||||
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ConvolveOp;
|
||||
import java.awt.image.Kernel;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -34,6 +38,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* - 侧栏颜色使用 #3C3F41(按你的要求)
|
||||
* - 搜索框以居中为主,聚焦时带放大动画与圆角外观
|
||||
* - 尽量避免离屏绘制遗留(使用 component.printAll() 截图)
|
||||
* - 添加背景图片和玻璃模糊效果支持
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class MainWindow extends JFrame {
|
||||
private static final Logger logger = LogManager.getLogger(MainWindow.class);
|
||||
@@ -56,27 +62,18 @@ public class MainWindow extends JFrame {
|
||||
private JPanel sideBar;
|
||||
private JPanel contentPanel;
|
||||
private RoundedSearchField searchField;
|
||||
private JLabel status;
|
||||
|
||||
// 背景图片相关字段
|
||||
private Image backgroundImage;
|
||||
private float blurAmount = 0.0f;
|
||||
private float backgroundOpacity = 1.0f;
|
||||
private BufferedImage cachedBlurredBackground;
|
||||
private Dimension cachedBackgroundSize;
|
||||
|
||||
// settings dialog
|
||||
private WindowsJDialog dialog;
|
||||
|
||||
// 侧边栏颜色(比面板背景稍暗)
|
||||
private static Color SIDEBAR_COLOR = Optional.ofNullable(UIManager.getColor("Panel.background"))
|
||||
.orElse(new Color(0x3C3F41));
|
||||
|
||||
// 卡片背景(深色模式适配,比侧边栏稍亮)
|
||||
private static Color CARD_BG = Optional.ofNullable(UIManager.getColor("control"))
|
||||
.map(bg -> ThemeColors.brighten(bg, 0.1f))
|
||||
.orElse(new Color(0x4A4D50));
|
||||
|
||||
// 卡片边框(使用系统边框色或稍亮颜色)
|
||||
private static Color CARD_BORDER = Optional.ofNullable(UIManager.getColor("controlHighlight"))
|
||||
.orElse(new Color(0x5C5F61));
|
||||
|
||||
// 文本颜色(直接使用系统主题的文本颜色)
|
||||
private static Color TEXT_COLOR = Optional.ofNullable(UIManager.getColor("textText"))
|
||||
.orElse(new Color(0xE0E0E0));
|
||||
|
||||
public MainWindow() {
|
||||
// 增强字体设置逻辑:优先使用系统支持的中文字体
|
||||
String[] fontNames = {"Microsoft YaHei", "微软雅黑", "PingFang SC", "SimHei", "宋体", "新宋体", "SansSerif"};
|
||||
@@ -123,13 +120,132 @@ public class MainWindow extends JFrame {
|
||||
if (layeredPane != null && cardsPanel != null) {
|
||||
cardsPanel.setBounds(0, 0, layeredPane.getWidth(), layeredPane.getHeight());
|
||||
}
|
||||
// 窗口大小改变时重新生成模糊背景
|
||||
if (backgroundImage != null) {
|
||||
cachedBlurredBackground = null;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setLocationRelativeTo(null);
|
||||
//setLocationRelativeTo(null);
|
||||
|
||||
addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentShown(ComponentEvent e) {
|
||||
// 确保只执行一次
|
||||
removeComponentListener(this);
|
||||
setLocationRelativeTo(null);
|
||||
}
|
||||
});
|
||||
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
setSize(1200, 800);
|
||||
setSize(1060, 670);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置背景图片和玻璃模糊效果
|
||||
* @param backgroundImage 背景图片
|
||||
* @param blurAmount 模糊程度 (0.0f - 1.0f),0为不模糊,1为最大模糊
|
||||
* @param opacity 透明度 (0.0f - 1.0f),0为完全透明,1为完全不透明
|
||||
*/
|
||||
public void setBackgroundWithGlassEffect(Image backgroundImage, float blurAmount, float opacity) {
|
||||
this.backgroundImage = backgroundImage;
|
||||
this.blurAmount = Math.max(0.0f, Math.min(1.0f, blurAmount));
|
||||
this.backgroundOpacity = Math.max(0.0f, Math.min(1.0f, opacity));
|
||||
this.cachedBlurredBackground = null;
|
||||
this.cachedBackgroundSize = null;
|
||||
|
||||
// 重新绘制窗口
|
||||
//if (AxisInnovatorsBox.getMain().isWindow())
|
||||
// AxisInnovatorsBox.getMain().reloadAllWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除背景图片
|
||||
*/
|
||||
public void removeBackground() {
|
||||
this.backgroundImage = null;
|
||||
this.cachedBlurredBackground = null;
|
||||
this.cachedBackgroundSize = null;
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用高斯模糊到图片
|
||||
*/
|
||||
private BufferedImage applyGaussianBlur(BufferedImage image, float blurFactor) {
|
||||
if (blurFactor <= 0.0f) return image;
|
||||
|
||||
// 根据模糊因子计算模糊半径 (1-15像素)
|
||||
int radius = Math.max(1, (int)(blurFactor * 15));
|
||||
|
||||
// 确保半径为奇数
|
||||
if (radius % 2 == 0) radius++;
|
||||
|
||||
int size = radius * 2 + 1;
|
||||
float[] data = new float[size * size];
|
||||
|
||||
// 计算高斯核
|
||||
float sigma = radius / 3.0f;
|
||||
float twoSigmaSquare = 2.0f * sigma * sigma;
|
||||
float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI);
|
||||
float total = 0.0f;
|
||||
|
||||
for (int i = -radius; i <= radius; i++) {
|
||||
for (int j = -radius; j <= radius; j++) {
|
||||
float distance = i * i + j * j;
|
||||
data[(i + radius) * size + (j + radius)] =
|
||||
(float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot;
|
||||
total += data[(i + radius) * size + (j + radius)];
|
||||
}
|
||||
}
|
||||
|
||||
// 归一化
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] /= total;
|
||||
}
|
||||
|
||||
Kernel kernel = new Kernel(size, size, data);
|
||||
ConvolveOp convolve = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
|
||||
return convolve.filter(image, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建带模糊效果的背景图片
|
||||
*/
|
||||
private BufferedImage createBlurredBackground(Dimension size) {
|
||||
if (backgroundImage == null) return null;
|
||||
|
||||
// 如果尺寸相同且已有缓存,直接返回缓存
|
||||
if (cachedBlurredBackground != null && cachedBackgroundSize != null &&
|
||||
cachedBackgroundSize.equals(size)) {
|
||||
return cachedBlurredBackground;
|
||||
}
|
||||
|
||||
// 创建背景图片
|
||||
BufferedImage bgImage = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = bgImage.createGraphics();
|
||||
|
||||
// 设置渲染质量
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
|
||||
// 绘制原始背景图片(缩放以适应窗口)
|
||||
g2d.drawImage(backgroundImage, 0, 0, size.width, size.height, null);
|
||||
g2d.dispose();
|
||||
|
||||
// 应用模糊效果
|
||||
if (blurAmount > 0.0f) {
|
||||
bgImage = applyGaussianBlur(bgImage, blurAmount);
|
||||
}
|
||||
|
||||
// 缓存结果
|
||||
cachedBlurredBackground = bgImage;
|
||||
cachedBackgroundSize = new Dimension(size);
|
||||
|
||||
return bgImage;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,13 +274,56 @@ public class MainWindow extends JFrame {
|
||||
|
||||
setTitle(LanguageManager.getLoadedLanguages().getText("mainWindow.title"));
|
||||
|
||||
// 主容器
|
||||
JPanel mainPanel = new JPanel(new BorderLayout());
|
||||
// 主容器 - 使用自定义面板以支持背景绘制
|
||||
JPanel mainPanel = new JPanel(new BorderLayout()) {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
|
||||
// 如果有背景图片,绘制背景
|
||||
if (backgroundImage != null) {
|
||||
Graphics2D g2d = (Graphics2D) g.create();
|
||||
|
||||
// 设置透明度
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, backgroundOpacity));
|
||||
|
||||
// 获取带模糊效果的背景
|
||||
Dimension size = getSize();
|
||||
BufferedImage bg = createBlurredBackground(size);
|
||||
|
||||
if (bg != null) {
|
||||
g2d.drawImage(bg, 0, 0, null);
|
||||
}
|
||||
|
||||
g2d.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder());
|
||||
mainPanel.setOpaque(true);
|
||||
Color panelBg = UIManager.getColor("Panel.background");
|
||||
if (panelBg == null) panelBg = new Color(245, 246, 248);
|
||||
mainPanel.setBackground(panelBg);
|
||||
|
||||
// 设置背景色(如果有背景图片,则使用半透明背景)
|
||||
if (backgroundImage != null) {
|
||||
// 当有背景图片时,使用半透明的背景色
|
||||
Color panelBg = UIManager.getColor("Panel.background");
|
||||
if (panelBg != null) {
|
||||
// 降低背景色的不透明度,让背景图片透出来
|
||||
Color semiTransparentBg = new Color(
|
||||
panelBg.getRed(),
|
||||
panelBg.getGreen(),
|
||||
panelBg.getBlue(),
|
||||
(int)(200 * backgroundOpacity) // 调整透明度
|
||||
);
|
||||
mainPanel.setBackground(semiTransparentBg);
|
||||
}
|
||||
} else {
|
||||
// 没有背景图片时使用正常背景色
|
||||
Color panelBg = UIManager.getColor("Panel.background");
|
||||
if (panelBg == null) panelBg = new Color(245, 246, 248);
|
||||
mainPanel.setBackground(panelBg);
|
||||
}
|
||||
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
|
||||
|
||||
mainPanel.add(createHeader(), BorderLayout.NORTH);
|
||||
@@ -288,7 +447,19 @@ public class MainWindow extends JFrame {
|
||||
JComponent contentPane = (JComponent) content;
|
||||
Color panelBg = UIManager.getColor("Panel.background");
|
||||
if (panelBg == null) panelBg = new Color(245, 246, 248);
|
||||
contentPane.setBackground(panelBg);
|
||||
|
||||
// 如果有背景图片,使用半透明背景
|
||||
if (backgroundImage != null) {
|
||||
Color semiTransparentBg = new Color(
|
||||
panelBg.getRed(),
|
||||
panelBg.getGreen(),
|
||||
panelBg.getBlue(),
|
||||
(int)(200 * backgroundOpacity)
|
||||
);
|
||||
contentPane.setBackground(semiTransparentBg);
|
||||
} else {
|
||||
contentPane.setBackground(panelBg);
|
||||
}
|
||||
contentPane.repaint();
|
||||
}
|
||||
|
||||
@@ -309,7 +480,12 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
|
||||
if (sideBar != null) {
|
||||
sideBar.setBackground(getSidebarColor());
|
||||
if (backgroundImage != null) {
|
||||
sideBar.setOpaque(false);
|
||||
} else {
|
||||
sideBar.setOpaque(true);
|
||||
sideBar.setBackground(getSidebarColor());
|
||||
}
|
||||
sideBar.repaint();
|
||||
}
|
||||
|
||||
@@ -539,7 +715,7 @@ public class MainWindow extends JFrame {
|
||||
private JPanel createSideBar() {
|
||||
JPanel sidebar = new JPanel(new BorderLayout());
|
||||
sidebar.setOpaque(true);
|
||||
sidebar.setBackground(SIDEBAR_COLOR);
|
||||
//sidebar.setBackground(SIDEBAR_COLOR);
|
||||
sidebar.setPreferredSize(new Dimension(220, getHeight()));
|
||||
sidebar.setBorder(null);
|
||||
|
||||
@@ -574,7 +750,7 @@ public class MainWindow extends JFrame {
|
||||
JScrollPane listScroll = new JScrollPane(listPanel);
|
||||
listScroll.setBorder(null);
|
||||
listScroll.setOpaque(false);
|
||||
listScroll.getViewport().setOpaque(false);
|
||||
listScroll.getViewport().setOpaque(false); // 确保视口透明
|
||||
listScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
listScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
|
||||
listScroll.getVerticalScrollBar().setUI(new CustomScrollBarUI());
|
||||
@@ -680,7 +856,8 @@ public class MainWindow extends JFrame {
|
||||
));
|
||||
|
||||
// 样式
|
||||
button.setForeground(TEXT_COLOR);
|
||||
button.setForeground(Optional.ofNullable(UIManager.getColor("textText"))
|
||||
.orElse(new Color(0xE0E0E0)));
|
||||
// 初始设为不填充,由 updateSelection 决定是否填充背景
|
||||
button.setOpaque(false);
|
||||
button.setContentAreaFilled(true); // 允许根据 opaque/background 填充(mouse/selected 状态会切换 opaque)
|
||||
@@ -778,6 +955,14 @@ public class MainWindow extends JFrame {
|
||||
Color cardBorder = getCardBorder();
|
||||
Color shadowColor = isDarkTheme() ? new Color(30, 30, 30) : Color.BLACK;
|
||||
|
||||
// 如果有背景图片,卡片背景使用半透明
|
||||
if (backgroundImage != null) {
|
||||
cardBg = new Color(cardBg.getRed(), cardBg.getGreen(), cardBg.getBlue(),
|
||||
(int)(200 * backgroundOpacity));
|
||||
cardBorder = new Color(cardBorder.getRed(), cardBorder.getGreen(),
|
||||
cardBorder.getBlue(), (int)(200 * backgroundOpacity));
|
||||
}
|
||||
|
||||
// 1. 绘制阴影(根据主题调整透明度)
|
||||
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, isDarkTheme() ? 0.12f : 0.06f));
|
||||
g2.setColor(shadowColor);
|
||||
@@ -826,8 +1011,10 @@ public class MainWindow extends JFrame {
|
||||
titleLabel.setForeground(UIManager.getColor("Label.foreground"));
|
||||
JTextArea descArea = new JTextArea(tool.getDescription());
|
||||
descArea.setFont(new Font(selectFont("Segoe UI", "Microsoft YaHei", "SansSerif", 13).getName(), Font.PLAIN, 13));
|
||||
titleLabel.setForeground(TEXT_COLOR);
|
||||
descArea.setForeground(TEXT_COLOR.darker());
|
||||
titleLabel.setForeground(Optional.ofNullable(UIManager.getColor("textText"))
|
||||
.orElse(new Color(0xE0E0E0)));
|
||||
descArea.setForeground(Optional.ofNullable(UIManager.getColor("textText"))
|
||||
.orElse(new Color(0xE0E0E0)).darker());
|
||||
descArea.setForeground(new Color(100,100,105));
|
||||
descArea.setLineWrap(true);
|
||||
descArea.setWrapStyleWord(true);
|
||||
@@ -925,6 +1112,7 @@ public class MainWindow extends JFrame {
|
||||
card.repaint();
|
||||
if (currentElevation == targetElevation && Math.abs(cardScales.get(card) - targetScale) < 0.002f) ((Timer)e.getSource()).stop();
|
||||
}
|
||||
|
||||
}).start();
|
||||
}
|
||||
|
||||
@@ -1035,13 +1223,21 @@ public class MainWindow extends JFrame {
|
||||
JPanel footer = new JPanel(new BorderLayout());
|
||||
footer.setOpaque(false);
|
||||
footer.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||
JLabel status = new JLabel("Ready");
|
||||
status = new JLabel("Ready");
|
||||
status.setFont(new Font(selectFont("Segoe UI", "Microsoft YaHei", "SansSerif", 12).getName(), Font.PLAIN, 12));
|
||||
status.setForeground(UIManager.getColor("Label.foreground"));
|
||||
footer.add(status, BorderLayout.WEST);
|
||||
return footer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新状态
|
||||
* @param text 状态名称
|
||||
*/
|
||||
public void updataStatus(String text) {
|
||||
status.setText(text);
|
||||
}
|
||||
|
||||
private void updateSideSelection(String categoryId) {
|
||||
for (Map.Entry<String, JButton> e : sideButtons.entrySet()) {
|
||||
String id = e.getKey();
|
||||
@@ -1138,36 +1334,127 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- showSettings 恢复实现 ----------
|
||||
// ---------- showSettings 实现 ----------
|
||||
public void showSettings() {
|
||||
if (dialog == null || AxisInnovatorsBox.getMain().isWindowStartup(dialog)) {
|
||||
dialog = new WindowsJDialog(this,
|
||||
LanguageManager.getLoadedLanguages().getText("mainWindow.settings.title"), true);
|
||||
}
|
||||
dialog.setTitle(LanguageManager.getLoadedLanguages().getText("mainWindow.settings.title"));
|
||||
dialog.setSize(680, 480);
|
||||
dialog.setSize(750, 550);
|
||||
dialog.setLocationRelativeTo(this);
|
||||
|
||||
JPanel content = new JPanel(new BorderLayout());
|
||||
content.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
|
||||
content.setOpaque(false);
|
||||
|
||||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
// 使用 JLayer + LayerUI 来对整个内容做统一的淡入 + 下滑(仿 Apple 风格)动画,
|
||||
// 这样子组件也会跟随一起动画,而不是只有背景绘制发生变化。
|
||||
JPanel inner = new JPanel(new BorderLayout());
|
||||
inner.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
|
||||
inner.setOpaque(false); // 背景在 LayerUI 中绘制以便做阴影/圆角
|
||||
|
||||
|
||||
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.LEFT);
|
||||
tabbedPane.setOpaque(false);
|
||||
tabbedPane.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
|
||||
|
||||
List<RegistrationSettingsItem> registrationSettingsItemList
|
||||
= RegistrationSettingsItem.getRegistrationSettingsItemList();
|
||||
for (RegistrationSettingsItem registrationSettingsItem : registrationSettingsItemList) {
|
||||
registrationSettingsItem.registration(tabbedPane);
|
||||
}
|
||||
|
||||
content.add(tabbedPane, BorderLayout.CENTER);
|
||||
GlobalEventBus.EVENT_BUS.post(new SettingsLoadEvents(dialog, content));
|
||||
dialog.add(content);
|
||||
inner.add(tabbedPane, BorderLayout.CENTER);
|
||||
GlobalEventBus.EVENT_BUS.post(new SettingsLoadEvents(dialog, inner));
|
||||
|
||||
// LayerUI 实现:负责绘制圆角背景、阴影,并在 paint 中使用 alpha + translate 来实现动画
|
||||
class FadeLayerUI extends LayerUI<JComponent> {
|
||||
private float alpha = 0f;
|
||||
private int translateY = 20;
|
||||
|
||||
public void setAlpha(float alpha) { this.alpha = Math.max(0f, Math.min(1f, alpha)); }
|
||||
public void setTranslateY(int y) { this.translateY = y; }
|
||||
|
||||
@Override
|
||||
public void paint (Graphics g, JComponent c) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
int w = c.getWidth();
|
||||
int h = c.getHeight();
|
||||
|
||||
// 平滑处理
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// 先绘制柔和阴影(透明度与 alpha 关联)
|
||||
int arc = 16;
|
||||
float shadowAlpha = Math.min(0.35f, alpha * 0.35f);
|
||||
if (shadowAlpha > 0f) {
|
||||
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, shadowAlpha));
|
||||
g2.setColor(new Color(0, 0, 0, 255));
|
||||
// 画一个稍微向下偏移的矩形作为阴影基础
|
||||
g2.fillRoundRect(8, 10 + translateY/3, w - 16, h - 20, arc, arc);
|
||||
}
|
||||
|
||||
// 应用下滑位移 + 透明度到后续的内容绘制(包括子组件)
|
||||
g2.translate(0, translateY);
|
||||
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
|
||||
|
||||
Color panelBg = UIManager.getColor("Panel.background");
|
||||
if (panelBg == null)
|
||||
panelBg = new Color(245, 246, 248);
|
||||
// 绘制半透明/圆角的主背景
|
||||
g2.setColor(panelBg);
|
||||
g2.fillRoundRect(0, 0, w, h, arc, arc);
|
||||
|
||||
// 把转换后的 Graphics 传给 super.paint 来绘制子组件(子组件会被 alpha & translate 影响)
|
||||
super.paint(g2, c);
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
FadeLayerUI fadeUI = new FadeLayerUI();
|
||||
JLayer<JComponent> layered = new JLayer<>(inner, fadeUI);
|
||||
layered.setOpaque(false);
|
||||
|
||||
dialog.getContentPane().removeAll();
|
||||
dialog.getContentPane().setBackground(new Color(0,0,0,0)); // 保持透明(视平台支持情况)
|
||||
dialog.add(layered);
|
||||
|
||||
// 动画 Timer:350ms 的淡入 + 下滑(使用 ease-out 曲线)
|
||||
final int duration = 350; // ms
|
||||
final long start = System.currentTimeMillis();
|
||||
Timer timer = new Timer(16, null); // ~60fps
|
||||
timer.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
float t = (System.currentTimeMillis() - start) / (float) duration;
|
||||
if (t >= 1f) t = 1f;
|
||||
// ease-out (cubic)
|
||||
float eased = 1 - (float) Math.pow(1 - t, 3);
|
||||
|
||||
fadeUI.setAlpha(eased);
|
||||
// translate 从 20 -> 0
|
||||
fadeUI.setTranslateY((int) ((1 - eased) * 20));
|
||||
|
||||
layered.repaint();
|
||||
|
||||
if (t >= 1f) {
|
||||
((Timer) e.getSource()).stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
timer.setCoalesce(true);
|
||||
|
||||
// 初始状态隐藏内容(alpha=0),启动动画
|
||||
fadeUI.setAlpha(0f);
|
||||
fadeUI.setTranslateY(20);
|
||||
layered.setVisible(true);
|
||||
timer.start();
|
||||
|
||||
dialog.revalidate();
|
||||
if (AxisInnovatorsBox.getMain().isWindowStartup(dialog)) {
|
||||
AxisInnovatorsBox.getMain().popupWindow(dialog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------- 内部类:ToolCategory, ToolItem 等(保持原结构) ----------
|
||||
public static class ToolCategory {
|
||||
private final String name;
|
||||
@@ -1365,4 +1652,4 @@ public class MainWindow extends JFrame {
|
||||
dim.height += rowHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user