Compare commits
16 Commits
window-axi
...
52231cc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52231ccb22 | ||
|
|
20567a6211 | ||
|
|
be88f3829a | ||
|
|
2598e25168 | ||
|
|
c276e35204 | ||
|
|
7d07e6d0e1 | ||
|
|
1a1750d5a6 | ||
|
|
78bae01544 | ||
|
|
a41b894ee8 | ||
|
|
62c521a5ea | ||
|
|
5da71f05e7 | ||
|
|
9136ad8827 | ||
|
|
a5b3b90249 | ||
|
|
692ec3dc8d | ||
|
|
e4761d34e0 | ||
|
|
9d684b310f |
2
.idea/encodings.xml
generated
@@ -4,7 +4,7 @@
|
||||
<file url="file://$PROJECT_DIR$/language" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/plug-in/python/Testing/main.py" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/Cpp/LM/org_tzd_lm_LM.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/java/com/axis/innovators/box/gui/FridaWindow.java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/java/com/axis/innovators/box/window/FridaWindow.java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/java/org/tzd/lm/LM.java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/java/org/tzd/lm/LMApi.java" charset="UTF-8" />
|
||||
</component>
|
||||
|
||||
237
build.gradle
@@ -5,10 +5,12 @@ plugins {
|
||||
id 'org.openjfx.javafxplugin' version '0.1.0'
|
||||
id 'org.springframework.boot' version '3.2.0'
|
||||
id 'io.spring.dependency-management' version '1.1.4'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false // 关闭 shadow
|
||||
}
|
||||
|
||||
configurations {
|
||||
all*.exclude group: 'org.openjfx', module: 'javafx'
|
||||
proguardLib
|
||||
}
|
||||
|
||||
// JDK 版本检查
|
||||
@@ -22,237 +24,156 @@ group = 'com.axis.innovators.box'
|
||||
version = '0.0.1'
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://maven.aliyun.com/repository/public'
|
||||
metadataSources {
|
||||
mavenPom()
|
||||
artifact()
|
||||
ignoreGradleMetadataRedirection()
|
||||
}
|
||||
}
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
|
||||
maven { url 'https://jitpack.io' }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
|
||||
}
|
||||
//tasks.named("bootJar") {
|
||||
// enabled = false
|
||||
//}
|
||||
|
||||
dependencies {
|
||||
proguardLib files('libs/proguard.jar')
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.10.0')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
implementation 'org.commonmark:commonmark:0.24.0'
|
||||
implementation 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.1'
|
||||
implementation 'com.google.code.gson:gson:2.8.9'
|
||||
|
||||
implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
|
||||
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
|
||||
implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.20.0'
|
||||
|
||||
implementation 'org.ow2.asm:asm:9.7.1'
|
||||
implementation 'org.ow2.asm:asm-commons:9.7.1'
|
||||
implementation 'org.ow2.asm:asm-analysis:9.7.1'
|
||||
implementation 'org.ow2.asm:asm-util:9.7.1'
|
||||
implementation 'org.ow2.asm:asm-tree:9.7.1'
|
||||
implementation 'net.bytebuddy:byte-buddy:1.17.6'
|
||||
|
||||
implementation 'org.jsoup:jsoup:1.17.2'
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.8.9'
|
||||
|
||||
//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' // 官方主题包
|
||||
|
||||
implementation 'com.formdev:flatlaf:3.2.1'
|
||||
implementation 'com.formdev:flatlaf-extras:3.2.1'
|
||||
implementation 'com.formdev:flatlaf-intellij-themes:3.2.1'
|
||||
implementation 'io.github.vincenzopalazzo:material-ui-swing:1.1.2'
|
||||
implementation 'org.python:jython-standalone:2.7.3'
|
||||
implementation 'org.graalvm.python:python-embedding:24.2.1'
|
||||
|
||||
implementation files('libs/JNC-1.0-jnc.jar')
|
||||
implementation files('libs/dog api 1.3.jar')
|
||||
|
||||
implementation 'org.fxmisc.richtext:richtextfx:0.11.0' // 更新后的richtextfx
|
||||
implementation 'org.bitbucket.mstrobel:procyon-core:0.5.36' // 使用JitPack版本
|
||||
implementation files('libs/DesktopWallpaperSdk-1.0-SNAPSHOT.jar')
|
||||
implementation 'org.fxmisc.richtext:richtextfx:0.11.0'
|
||||
implementation 'org.bitbucket.mstrobel:procyon-core:0.5.36'
|
||||
implementation 'org.bitbucket.mstrobel:procyon-compilertools:0.5.36'
|
||||
|
||||
// 必须的核心依赖
|
||||
implementation 'com.fifesoft:rsyntaxtextarea:3.5.4'
|
||||
|
||||
// 可选UI增强
|
||||
implementation 'com.fifesoft:rstaui:3.3.1'
|
||||
implementation 'com.fifesoft:languagesupport:3.3.0'
|
||||
implementation 'com.fifesoft:autocomplete:3.3.2'
|
||||
|
||||
implementation 'org.apache.commons:commons-compress:1.23.0'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
|
||||
implementation 'org.controlsfx:controlsfx:11.1.2' // 现代化UI组件
|
||||
implementation 'com.dlsc.formsfx:formsfx-core:11.6.0' // 表单组件
|
||||
implementation 'net.sourceforge.plantuml:plantuml:8059' // UML支持(可选)
|
||||
|
||||
implementation 'org.controlsfx:controlsfx:11.1.2'
|
||||
implementation 'com.dlsc.formsfx:formsfx-core:11.6.0'
|
||||
implementation 'net.sourceforge.plantuml:plantuml:8059'
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
|
||||
implementation 'org.openjfx:javafx-controls:21'
|
||||
implementation 'org.benf:cfr:0.152'
|
||||
|
||||
implementation 'com.github.javaparser:javaparser-core:3.25.1'
|
||||
|
||||
implementation 'com.1stleg:jnativehook:2.1.0'
|
||||
|
||||
//implementation 'org.springframework.boot:spring-boot-starter-web' // Web支持
|
||||
//implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // JPA数据库支持
|
||||
//implementation 'org.springframework.boot:spring-boot-starter-validation' // 参数校验
|
||||
|
||||
// 安全相关依赖
|
||||
//implementation 'org.springframework.boot:spring-boot-starter-security' // Spring Security
|
||||
//implementation 'io.jsonwebtoken:jjwt-api:0.12.3' // JWT API
|
||||
//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'
|
||||
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'
|
||||
implementation 'org.bytedeco:javacpp-platform:1.5.7'
|
||||
implementation 'com.madgag:animated-gif-lib:1.4'
|
||||
implementation 'org.openjfx:javafx-web:17'
|
||||
runtimeOnly 'com.mysql:mysql-connector-j'
|
||||
|
||||
// 开发工具
|
||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||
|
||||
// 测试依赖
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testImplementation 'org.springframework.security:spring-security-test'
|
||||
|
||||
implementation 'com.kitfox.svg:svg-salamander:1.0'
|
||||
|
||||
implementation 'com.vladsch.flexmark:flexmark:0.64.8'
|
||||
|
||||
// SystemTray4J 核心库
|
||||
implementation 'com.github.kwhat:jnativehook:2.2.2'
|
||||
implementation 'com.dustinredmond.fxtrayicon:FXTrayIcon:4.0.1' // 增强版
|
||||
|
||||
// JavaFX 模块
|
||||
implementation 'com.dustinredmond.fxtrayicon:FXTrayIcon:4.0.1'
|
||||
implementation 'org.openjfx:javafx-graphics:21'
|
||||
|
||||
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 'com.alphacephei:vosk:0.3.45'
|
||||
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 'org.bitbucket.mstrobel:procyon-core:0.6.0'
|
||||
implementation 'org.bitbucket.mstrobel:procyon-compilertools:0.6.0'
|
||||
implementation 'com.belerweb:pinyin4j:2.5.1'
|
||||
|
||||
// 音频I/O
|
||||
implementation 'commons-io:commons-io:2.18.0'
|
||||
implementation 'jflac:jflac:1.3' // FLAC支持
|
||||
|
||||
implementation 'jflac:jflac:1.3'
|
||||
implementation 'com.github.axet:TarsosDSP:2.4'
|
||||
|
||||
implementation 'org.json:json:20231013'
|
||||
|
||||
// Eclipse 组件
|
||||
// https://mvnrepository.com/artifact/org.eclipse.swt/org.eclipse.swt.win32.win32.x86_64
|
||||
//implementation 'org.eclipse.swt:org.eclipse.swt.win32.win32.x86_64:4.3'
|
||||
//
|
||||
//// 基础 Eclipse 组件
|
||||
//implementation 'org.eclipse.platform:org.eclipse.jface:3.36.0'
|
||||
//implementation 'org.eclipse.platform:org.eclipse.jface.text:3.27.0' // 使用兼容版本
|
||||
//implementation 'org.eclipse.platform:org.eclipse.core.runtime:3.33.0'
|
||||
//implementation 'org.eclipse.platform:org.eclipse.equinox.common:3.20.0'
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 分离依赖项到 libs 目录
|
||||
configurations.all {
|
||||
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
|
||||
}
|
||||
|
||||
// 复制依赖到 libs 目录
|
||||
task copyDependencies(type: Copy) {
|
||||
from configurations.runtimeClasspath
|
||||
into "$buildDir/libs/libs"
|
||||
}
|
||||
|
||||
// 执行我生成jar
|
||||
jar {
|
||||
manifest {
|
||||
attributes 'Main-Class': 'com.axis.innovators.box.Main',
|
||||
'Class-Path': configurations.runtimeClasspath.files.collect { "libs/$it.name" }.join(' ')
|
||||
}
|
||||
// 原始 jar 打包(不含依赖)
|
||||
tasks.jar {
|
||||
dependsOn copyDependencies
|
||||
archiveBaseName.set("${rootProject.name}")
|
||||
archiveVersion.set("${version}")
|
||||
}
|
||||
|
||||
// 测试配置
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
systemProperty "java.system.class.loader", "com.axis.innovators.box.plugins.BoxClassLoader"
|
||||
// ProGuard 混淆任务
|
||||
task obfuscateJar(type: JavaExec) {
|
||||
dependsOn jar
|
||||
group = "build"
|
||||
description = "使用 ProGuard 混淆并生成映射表"
|
||||
|
||||
// 确保测试能看到依赖
|
||||
classpath = files(sourceSets.test.output) +
|
||||
files("$buildDir/libs/libs") +
|
||||
configurations.testRuntimeClasspath
|
||||
}
|
||||
mainClass = 'proguard.ProGuard'
|
||||
classpath = configurations.proguardLib
|
||||
|
||||
// 处理开源文档文件
|
||||
sourceSets {
|
||||
main {
|
||||
resources {
|
||||
exclude '**/*.md'
|
||||
}
|
||||
}
|
||||
openSourceDocs {
|
||||
resources {
|
||||
srcDir 'src/main/resources'
|
||||
include '**/*.md'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaExec).configureEach {
|
||||
jvmArgs = [
|
||||
'-Djava.system.class.loader=com.axis.innovators.box.plugins.BoxClassLoader'
|
||||
args = [
|
||||
'-injars', "$buildDir/libs/${rootProject.name}-${version}.jar",
|
||||
'-outjars', "$buildDir/libs/${rootProject.name}-${version}-obf.jar",
|
||||
'-libraryjars', "${System.getProperty('java.home')}/jmods/java.base.jmod",
|
||||
'-printmapping', "$buildDir/libs/output.srg",
|
||||
'-keep class com.axis.innovators.box.plugins.**',
|
||||
'-keep class com.axis.innovators.box.plugins.BoxClassLoader{*;}',
|
||||
'-keeppackagenames', 'com.axis.innovators.box',
|
||||
'-keeppackagenames', 'com.axis.innovators.box.plugins',
|
||||
'-keepnames', 'class com.axis.innovators.box.**',
|
||||
'-keepnames', 'class com.axis.innovators.box.plugins.**',
|
||||
'-dontwarn',
|
||||
'-dontoptimize',
|
||||
'-dontshrink',
|
||||
'-keepattributes', 'Signature,InnerClasses,EnclosingMethod,RuntimeVisibleAnnotations,RuntimeInvisibleAnnotations,RuntimeVisibleParameterAnnotations,RuntimeInvisibleParameterAnnotations,Deprecated,SourceFile,LineNumberTable,LocalVariableTable,LocalVariableTypeTable'
|
||||
]
|
||||
}
|
||||
|
||||
javafx {
|
||||
version = "21"
|
||||
modules = [ 'javafx.controls', 'javafx.fxml' ]
|
||||
build {
|
||||
dependsOn obfuscateJar
|
||||
}
|
||||
|
||||
// 单独打包文档
|
||||
task packageOpenSourceDocs(type: Jar) {
|
||||
archiveClassifier = 'docs'
|
||||
from sourceSets.openSourceDocs.resources
|
||||
destinationDirectory = file("$buildDir/libs/docs")
|
||||
}
|
||||
|
||||
// 完整的 application 配置
|
||||
application {
|
||||
mainClass = 'com.axis.innovators.box.Main'
|
||||
}
|
||||
|
||||
// 确保运行时参数生效
|
||||
applicationDefaultJvmArgs = [
|
||||
"-Djava.system.class.loader=com.axis.innovators.box.plugins.BoxClassLoader",
|
||||
"-Dloader.library.path=$buildDir/libs/libs",
|
||||
'-Dfile.encoding=UTF-8'
|
||||
task runClient(type: JavaExec) {
|
||||
group = "application"
|
||||
description = "运行 com.axis.innovators.box.Main"
|
||||
classpath = sourceSets.main.runtimeClasspath
|
||||
mainClass = "com.axis.innovators.box.Main"
|
||||
jvmArgs = [
|
||||
"-Dfile.encoding=UTF-8",
|
||||
"-Djava.system.class.loader=com.axis.innovators.box.plugins.BoxClassLoader"
|
||||
]
|
||||
}
|
||||
|
||||
// 创建可运行分发
|
||||
tasks.register('release') {
|
||||
dependsOn build, packageOpenSourceDocs
|
||||
|
||||
doLast {
|
||||
copy {
|
||||
from "$buildDir/libs"
|
||||
into "$buildDir/dist"
|
||||
include '*.jar'
|
||||
include 'docs/**'
|
||||
include 'libs/**'
|
||||
}
|
||||
println "Release package ready at: $buildDir/dist"
|
||||
}
|
||||
}
|
||||
|
||||
// 默认构建任务
|
||||
build.dependsOn release
|
||||
@@ -1,3 +1,3 @@
|
||||
#Current Loaded Language
|
||||
#Sat May 31 10:30:35 CST 2025
|
||||
#Sat Aug 16 18:11:03 CST 2025
|
||||
loadedLanguage=system\:zh_CN
|
||||
|
||||
@@ -1,9 +1,43 @@
|
||||
default_theme.system.topicName=\u9ED8\u8BA4\u4E3B\u9898
|
||||
default_theme.default.tip=\u9ED8\u8BA4\u7684\u4E3B\u9898
|
||||
metal_theme.system.topicName=Metal\u98CE\u683C
|
||||
metal_theme.default.tip=Metal\u98CE\u683C
|
||||
motif_theme.system.topicName=Motif\u98CE\u683C
|
||||
motif_theme.default.tip=Motif\u98CE\u683C
|
||||
# \u9ED8\u8BA4\u4E3B\u9898
|
||||
default_theme.system.topicName=\u7CFB\u7EDF\u9ED8\u8BA4\u4E3B\u9898
|
||||
default_theme.default.tip=\u4F7F\u7528\u64CD\u4F5C\u7CFB\u7EDF\u9ED8\u8BA4\u5916\u89C2
|
||||
|
||||
# Metal \u4E3B\u9898
|
||||
metal_theme.system.topicName=Metal \u4E3B\u9898 (Java \u7ECF\u5178)
|
||||
metal_theme.default.tip=Java \u9ED8\u8BA4\u7684 Metal \u98CE\u683C\u754C\u9762
|
||||
|
||||
# Motif \u4E3B\u9898
|
||||
motif_theme.system.topicName=Motif \u4E3B\u9898 (UNIX \u98CE\u683C)
|
||||
motif_theme.default.tip=\u4F20\u7EDF\u7684 UNIX Motif \u5916\u89C2
|
||||
|
||||
# FlatLaf \u4E3B\u9898
|
||||
flatLight_theme.system.topicName=Flat Light (\u6D45\u8272)
|
||||
flatLight_theme.default.tip=\u73B0\u4EE3\u5316\u6D45\u8272\u4E3B\u9898\uFF0C\u7B80\u6D01\u660E\u4EAE
|
||||
|
||||
flatDark_theme.system.topicName=Flat Dark (\u6DF1\u8272)
|
||||
flatDark_theme.default.tip=\u73B0\u4EE3\u5316\u6DF1\u8272\u4E3B\u9898\uFF0C\u62A4\u773C\u8212\u9002
|
||||
|
||||
flatIntelliJ_theme.system.topicName=IntelliJ \u6D45\u8272
|
||||
flatIntelliJ_theme.default.tip=\u7C7B\u4F3C IntelliJ IDEA \u7684\u6D45\u8272\u4E3B\u9898
|
||||
|
||||
flatDarcula_theme.system.topicName=Darcula \u6DF1\u8272
|
||||
flatDarcula_theme.default.tip=\u7C7B\u4F3C IntelliJ IDEA \u7684 Darcula \u6DF1\u8272\u4E3B\u9898
|
||||
|
||||
flatMacLight_theme.system.topicName=macOS \u6D45\u8272
|
||||
flatMacLight_theme.default.tip=\u7C7B\u4F3C macOS \u7684\u6D45\u8272\u98CE\u683C
|
||||
|
||||
flatMacDark_theme.system.topicName=macOS \u6DF1\u8272
|
||||
flatMacDark_theme.default.tip=\u7C7B\u4F3C macOS \u7684\u6DF1\u8272\u6A21\u5F0F\uFF0C\u4F18\u96C5\u73B0\u4EE3
|
||||
|
||||
mars_dark_theme.system.topicName=MaterialLookAndFeel\u7684\u6697\u8272
|
||||
mars_dark_theme.default.tip=\u57FA\u4E8E MaterialLookAndFeel \u7684\u6697\u8272\u4E3B\u9898
|
||||
|
||||
material_lite_theme.system.topicName=MaterialLookAndFeel\u7684\u6D45\u8272
|
||||
material_lite_theme.default.tip=\u57FA\u4E8E MaterialLookAndFeel \u7684\u6D45\u8272\u4E3B\u9898\uFF0CMaterial Design \u98CE\u683C
|
||||
|
||||
material_oceanic_theme.system.topicName=MaterialLookAndFeel\u7684\u6DF1\u8272
|
||||
material_oceanic_theme.default.tip=\u57FA\u4E8E MaterialLookAndFeel \u7684\u6D45\u8272\u4E3B\u9898\uFF0CMaterial Design \u98CE\u683C
|
||||
|
||||
flatLightLaf_theme.system.topicName=flatLightLaf\u98CE\u683C
|
||||
flatLightLaf_theme.default.tip=flatLightLaf\u98CE\u683C
|
||||
|
||||
@@ -71,6 +105,66 @@ localWindow.prompt.system=\u4F60\u662F\u4E00\u4E2A\u4E50\u4E8E\u52A9\u4EBA\u7684
|
||||
localWindow.prompt.system.2=\u4F60\u662F\u4E00\u4E2A\u4E13\u6CE8\u68C0\u7D22\u641C\u7D22\u4FE1\u606F\u7684\u52A9\u624B\uFF0C\u7528\u6237\u5C06\u63D0\u4F9B\u4E00\u4E9B\u94FE\u63A5\u3001\u6458\u8981\u3001\u6807\u9898\u3001\u722C\u53D6\u5230\u7684\u5185\u5BB9\uFF0C\u4F60\u7684\u4F5C\u7528\u5C31\u662F\u8F93\u51FA\u4E3B\u8981\u5185\u5BB9\u4E0E\u7528\u6237\u95EE\u9898\u76F8\u5173\u7684\u5185\u5BB9\u3002
|
||||
localWindow.prompt.name=\u3010\u7528\u6237\u3011
|
||||
|
||||
# \u8F93\u5165\u6846\u63D0\u793A
|
||||
fridaWindow.pid.placeholder=\u8F93\u5165\u8FDB\u7A0BPID
|
||||
fridaWindow.process.tooltip=\u9009\u62E9\u76EE\u6807\u8FDB\u7A0B
|
||||
|
||||
# \u9762\u677F\u6807\u9898
|
||||
fridaWindow.script.title=\u811A\u672C\u7F16\u8F91\u5668
|
||||
fridaWindow.log.title=\u8F93\u51FA\u65E5\u5FD7
|
||||
|
||||
# \u8FDB\u7A0B\u76F8\u5173
|
||||
fridaWindow.process.name=\u8FDB\u7A0B\u540D\u79F0
|
||||
fridaWindow.process.pid=\u8FDB\u7A0BID
|
||||
fridaWindow.processContext.inject=\u6CE8\u5165\u6B64\u8FDB\u7A0B
|
||||
fridaWindow.search.placeholder=\u641C\u7D22\u8FDB\u7A0B...
|
||||
|
||||
# \u9519\u8BEF\u63D0\u793A
|
||||
fridaWindow.error.title=\u9519\u8BEF
|
||||
fridaWindow.error.noPid=\u8BF7\u5148\u8F93\u5165\u8FDB\u7A0BPID
|
||||
fridaWindow.error.invalidPid=\u65E0\u6548\u7684\u8FDB\u7A0BID
|
||||
fridaWindow.error.injectFail=\u6CE8\u5165\u5931\u8D25
|
||||
fridaWindow.error.open=\u6587\u4EF6\u6253\u5F00\u5931\u8D25
|
||||
fridaWindow.error.save=\u6587\u4EF6\u4FDD\u5B58\u5931\u8D25
|
||||
|
||||
# \u6587\u4EF6\u8FC7\u6EE4
|
||||
fridaWindow.fileFilter=Frida\u811A\u672C\u6587\u4EF6 (*.frida)
|
||||
|
||||
# \u8BBE\u7F6E\u7A97\u53E3
|
||||
fridaWindow.settings.tab.color=\u8BED\u6CD5\u9AD8\u4EAE
|
||||
fridaWindow.settings.tab.font=\u5B57\u4F53\u8BBE\u7F6E
|
||||
fridaWindow.settings.save=\u4FDD\u5B58
|
||||
fridaWindow.settings.default=\u6062\u590D\u9ED8\u8BA4
|
||||
fridaWindow.settings.cancel=\u53D6\u6D88
|
||||
fridaWindow.chooseColor=\u9009\u62E9\u989C\u8272
|
||||
|
||||
# \u8BED\u6CD5\u9AD8\u4EAE\u8BBE\u7F6E
|
||||
fridaWindow.col.syntax=\u8BED\u6CD5\u5143\u7D20
|
||||
fridaWindow.col.color=\u989C\u8272
|
||||
fridaWindow.syntax.keyword=\u5173\u952E\u5B57
|
||||
fridaWindow.syntax.stdlib=\u6807\u51C6\u5E93
|
||||
fridaWindow.syntax.builtin=\u5185\u7F6E\u51FD\u6570
|
||||
fridaWindow.syntax.string=\u5B57\u7B26\u4E32
|
||||
fridaWindow.syntax.number=\u6570\u5B57
|
||||
fridaWindow.syntax.comment=\u6CE8\u91CA
|
||||
fridaWindow.syntax.operator=\u8FD0\u7B97\u7B26
|
||||
fridaWindow.syntax.identifier=\u6807\u8BC6\u7B26
|
||||
fridaWindow.syntax.separator=\u5206\u9694\u7B26
|
||||
fridaWindow.syntax.regex=\u6B63\u5219\u8868\u8FBE\u5F0F
|
||||
|
||||
# \u5B57\u4F53\u8BBE\u7F6E
|
||||
fridaWindow.font.name=\u5B57\u4F53\u540D\u79F0
|
||||
fridaWindow.font.size=\u5B57\u4F53\u5927\u5C0F
|
||||
fridaWindow.font.style=\u5B57\u4F53\u6837\u5F0F
|
||||
fridaWindow.font.bold=\u7C97\u4F53
|
||||
fridaWindow.font.italic=\u659C\u4F53
|
||||
fridaWindow.font.previewLabel=\u9884\u89C8
|
||||
fridaWindow.font.preview=\u793A\u4F8B\u6587\u672C\uFF1Aconsole.log("Hello Frida");
|
||||
|
||||
# \u5173\u4E8E\u7A97\u53E3
|
||||
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.2.title=\u57FA\u7840\u8BBE\u7F6E
|
||||
settings.3.title=\u5173\u4E8E
|
||||
|
||||
BIN
libs/DesktopWallpaperSdk-1.0-SNAPSHOT.jar
Normal file
BIN
libs/proguard.jar
Normal file
BIN
logo.ico
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 17 KiB |
@@ -3,7 +3,7 @@ package com.axis.innovators.box;
|
||||
import com.axis.innovators.box.events.GlobalEventBus;
|
||||
import com.axis.innovators.box.events.OpenFileEvents;
|
||||
import com.axis.innovators.box.events.StartupEvent;
|
||||
import com.axis.innovators.box.gui.*;
|
||||
import com.axis.innovators.box.window.*;
|
||||
import com.axis.innovators.box.plugins.PluginDescriptor;
|
||||
import com.axis.innovators.box.plugins.PluginLoader;
|
||||
import com.axis.innovators.box.plugins.PluginPyLoader;
|
||||
@@ -16,7 +16,13 @@ import com.axis.innovators.box.util.PythonResult;
|
||||
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.formdev.flatlaf.themes.FlatMacDarkLaf;
|
||||
import com.formdev.flatlaf.themes.FlatMacLightLaf;
|
||||
import com.sun.management.HotSpotDiagnosticMXBean;
|
||||
import mdlaf.MaterialLookAndFeel;
|
||||
import mdlaf.themes.JMarsDarkTheme;
|
||||
import mdlaf.themes.MaterialLiteTheme;
|
||||
import mdlaf.themes.MaterialOceanicTheme;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
@@ -78,6 +84,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 +368,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 +397,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 +446,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 +634,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)) {
|
||||
@@ -692,38 +719,132 @@ public class AxisInnovatorsBox {
|
||||
*/
|
||||
private void setTopic() {
|
||||
try {
|
||||
main.registrationTopic.addTopic(new com.formdev.flatlaf.FlatDarculaLaf(),
|
||||
"Darcula主题",
|
||||
"Darcula主题",
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:darcula_theme");
|
||||
|
||||
main.registrationTopic.addTopic(UIManager.getSystemLookAndFeelClassName(),
|
||||
boolean isDarkMode = SystemInfoUtil.isSystemDarkMode();
|
||||
|
||||
// 1. 默认系统主题
|
||||
main.registrationTopic.addTopic(
|
||||
UIManager.getSystemLookAndFeelClassName(),
|
||||
LanguageManager.getLoadedLanguages().getText("default_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("default_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:default_theme");
|
||||
main.registrationTopic.setLoading("system:default_theme");
|
||||
"system:default_theme",
|
||||
isDarkMode
|
||||
);
|
||||
|
||||
main.registrationTopic.addTopic("javax.swing.plaf.metal.MetalLookAndFeel",
|
||||
// 2. Metal (Java默认主题) - 浅色主题
|
||||
main.registrationTopic.addTopic(
|
||||
"javax.swing.plaf.metal.MetalLookAndFeel",
|
||||
LanguageManager.getLoadedLanguages().getText("metal_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("metal_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:metal_theme");
|
||||
"system:metal_theme",
|
||||
false
|
||||
);
|
||||
|
||||
main.registrationTopic.addTopic("com.sun.java.swing.plaf.motif.MotifLookAndFeel",
|
||||
// 3. Motif (UNIX风格) - 通常为浅色主题
|
||||
main.registrationTopic.addTopic(
|
||||
"com.sun.java.swing.plaf.motif.MotifLookAndFeel",
|
||||
LanguageManager.getLoadedLanguages().getText("motif_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("motif_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:motif_theme");
|
||||
"system:motif_theme",
|
||||
false
|
||||
);
|
||||
|
||||
main.registrationTopic.addTopic(new FlatLightLaf(),
|
||||
LanguageManager.getLoadedLanguages().getText("flatLightLaf_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("flatLightLaf_theme.default.tip"),
|
||||
// 4. FlatLaf 主题注册
|
||||
// 4.1 FlatLight (默认浅色)
|
||||
main.registrationTopic.addTopic(
|
||||
new com.formdev.flatlaf.FlatLightLaf(),
|
||||
LanguageManager.getLoadedLanguages().getText("flatLight_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("flatLight_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:flatLightLaf_theme");
|
||||
"system:flatLight_theme",
|
||||
false
|
||||
);
|
||||
|
||||
UIManager.setLookAndFeel(new com.formdev.flatlaf.FlatDarculaLaf());
|
||||
// 4.2 FlatDark (默认深色)
|
||||
main.registrationTopic.addTopic(
|
||||
new com.formdev.flatlaf.FlatDarkLaf(),
|
||||
LanguageManager.getLoadedLanguages().getText("flatDark_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("flatDark_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:flatDark_theme",
|
||||
true
|
||||
);
|
||||
|
||||
// 4.3 FlatIntelliJ (类似IDEA浅色)
|
||||
main.registrationTopic.addTopic(
|
||||
new com.formdev.flatlaf.FlatIntelliJLaf(),
|
||||
LanguageManager.getLoadedLanguages().getText("flatIntelliJ_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("flatIntelliJ_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:flatIntelliJ_theme",
|
||||
false
|
||||
);
|
||||
|
||||
// 4.4 FlatDarcula (类似IDEA深色)
|
||||
main.registrationTopic.addTopic(
|
||||
new com.formdev.flatlaf.FlatDarculaLaf(),
|
||||
LanguageManager.getLoadedLanguages().getText("flatDarcula_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("flatDarcula_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:flatDarcula_theme",
|
||||
true
|
||||
);
|
||||
|
||||
// 4.5 FlatMacLight (macOS风格浅色)
|
||||
main.registrationTopic.addTopic(
|
||||
new FlatMacLightLaf(),
|
||||
LanguageManager.getLoadedLanguages().getText("flatMacLight_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("flatMacLight_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:flatMacLight_theme",
|
||||
false
|
||||
);
|
||||
|
||||
// 4.6 FlatMacDark (macOS风格深色)
|
||||
main.registrationTopic.addTopic(
|
||||
new FlatMacDarkLaf(),
|
||||
LanguageManager.getLoadedLanguages().getText("flatMacDark_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("flatMacDark_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:flatMacDark_theme",
|
||||
true
|
||||
);
|
||||
|
||||
main.registrationTopic.addTopic(
|
||||
new MaterialLookAndFeel(new JMarsDarkTheme()),
|
||||
LanguageManager.getLoadedLanguages().getText("mars_dark_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("mars_dark_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:mars_dark_theme",
|
||||
true
|
||||
);
|
||||
|
||||
main.registrationTopic.addTopic(
|
||||
new MaterialLookAndFeel(new MaterialLiteTheme()),
|
||||
LanguageManager.getLoadedLanguages().getText("material_lite_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("material_lite_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:material_lite_theme",
|
||||
false
|
||||
);
|
||||
|
||||
main.registrationTopic.addTopic(
|
||||
new MaterialLookAndFeel(new MaterialOceanicTheme()),
|
||||
LanguageManager.getLoadedLanguages().getText("material_oceanic_theme.system.topicName"),
|
||||
LanguageManager.getLoadedLanguages().getText("material_oceanic_theme.default.tip"),
|
||||
LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
|
||||
"system:material_oceanic_theme",
|
||||
true
|
||||
);
|
||||
|
||||
LookAndFeel defaultLaf = isDarkMode ? new FlatMacDarkLaf() : new FlatMacLightLaf();
|
||||
UIManager.setLookAndFeel(defaultLaf);
|
||||
main.registrationTopic.setLoading(
|
||||
isDarkMode ? "system:flatMacDark_theme" : "system:flatMacLight_theme"
|
||||
);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to load the system facade class", e);
|
||||
}
|
||||
@@ -756,7 +877,6 @@ public class AxisInnovatorsBox {
|
||||
windowsJDialog.repaint();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 重新加载窗口
|
||||
*/
|
||||
@@ -769,10 +889,12 @@ public class AxisInnovatorsBox {
|
||||
for (WindowsJDialog windowsJDialog : windowsJDialogList) {
|
||||
windowsJDialog.getContentPane().removeAll();
|
||||
windowsJDialog.initUI();
|
||||
windowsJDialog.updateTheme();
|
||||
windowsJDialog.revalidate();
|
||||
windowsJDialog.repaint();
|
||||
}
|
||||
ex.initUI();
|
||||
ex.updateTheme();
|
||||
ex.revalidate();
|
||||
RegistrationSettingsItem.overloading();
|
||||
isWindow = true;
|
||||
@@ -805,6 +927,20 @@ 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 +1016,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 +1107,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ public class Log4j2OutputStream extends OutputStream {
|
||||
systemOutContent.write(b, off, len);
|
||||
String message = new String(b, off, len).trim();
|
||||
logger.info(message);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,19 +28,12 @@ public class Main {
|
||||
private static FileChannel lockChannel = null;
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (!acquireLock()) {
|
||||
JOptionPane.showMessageDialog(
|
||||
null,
|
||||
"程序已在运行中,无法启动多个实例",
|
||||
"错误",
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// 清理日志文件(最大日志为10)
|
||||
FolderCleaner.cleanFolder(FolderCreator.getLogsFolder(), 10);
|
||||
|
||||
// 加载保存的语言
|
||||
LanguageManager.loadSavedLanguage();
|
||||
// 如果没有加载的语言,则加载默认的语言
|
||||
if (LanguageManager.getLoadedLanguages() == null) {
|
||||
LanguageManager.loadLanguage("system:zh_CN");
|
||||
}
|
||||
@@ -83,7 +76,9 @@ public class Main {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!acquireLock()) {
|
||||
return;
|
||||
}
|
||||
AxisInnovatorsBox.run(args, debugWindowEnabled);
|
||||
}
|
||||
|
||||
@@ -126,10 +121,7 @@ public class Main {
|
||||
}
|
||||
}
|
||||
|
||||
// 添加JVM关闭钩子确保锁释放
|
||||
static {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
releaseLock();
|
||||
}));
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(Main::releaseLock));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.axis.innovators.box.browser;
|
||||
|
||||
import com.axis.innovators.box.events.BrowserCreationCallback;
|
||||
import org.cef.CefApp;
|
||||
import org.cef.CefClient;
|
||||
import org.cef.CefSettings;
|
||||
@@ -31,6 +32,7 @@ import static org.cef.callback.CefMenuModel.MenuId.MENU_ID_USER_FIRST;
|
||||
*/
|
||||
public class BrowserWindow extends JFrame {
|
||||
private final String windowId;
|
||||
private final String htmlUrl;
|
||||
private CefApp cefApp;
|
||||
private CefClient client;
|
||||
private CefBrowser browser;
|
||||
@@ -42,6 +44,7 @@ public class BrowserWindow extends JFrame {
|
||||
private CefMessageRouter msgRouter;
|
||||
|
||||
public static class Builder {
|
||||
private BrowserCreationCallback browserCreationCallback;
|
||||
private String windowId;
|
||||
private String title = "JCEF Window";
|
||||
private Dimension size = new Dimension(800, 600);
|
||||
@@ -51,7 +54,8 @@ public class BrowserWindow extends JFrame {
|
||||
private boolean resizable = true; // 默认允许调整大小
|
||||
private boolean maximizable = true; // 默认允许最大化
|
||||
private boolean minimizable = true; // 默认允许最小化
|
||||
|
||||
private String htmlUrl = "";
|
||||
private boolean openLinksInExternalBrowser = true; // 默认使用外部浏览器
|
||||
|
||||
public Builder resizable(boolean resizable) {
|
||||
this.resizable = resizable;
|
||||
@@ -63,6 +67,44 @@ public class BrowserWindow extends JFrame {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置链接打开方式
|
||||
*
|
||||
* @param openInBrowser 是否在当前浏览器窗口中打开链接
|
||||
* true - 在当前浏览器窗口中打开链接(本地跳转)
|
||||
* false - 使用系统默认浏览器打开链接(外部跳转)
|
||||
* @return Builder实例,支持链式调用
|
||||
*
|
||||
* @apiNote 此方法控制两种不同的链接打开行为:
|
||||
* 1. 当设置为true时:
|
||||
* - 所有链接将在当前CEF浏览器窗口内打开
|
||||
*
|
||||
* 2. 当设置为false时(默认值):
|
||||
* - 所有链接将在系统默认浏览器中打开
|
||||
* - 更安全,避免潜在的安全风险
|
||||
* - 适用于简单的信息展示场景
|
||||
*
|
||||
* @implNote 内部实现说明:
|
||||
* - 实际存储的是反向值(openLinksInExternalBrowser)
|
||||
* - 这样设置是为了保持与历史版本的兼容性
|
||||
* - 方法名使用"openInBrowser"更符合用户直觉
|
||||
*
|
||||
* @example 使用示例:
|
||||
* // 在当前窗口打开链接
|
||||
* new Builder().openLinksInBrowser(true).build();
|
||||
*
|
||||
* // 使用系统浏览器打开链接(默认)
|
||||
* new Builder().openLinksInBrowser(false).build();
|
||||
*
|
||||
* @see #openLinksInExternalBrowser 内部存储字段
|
||||
* @see CefLifeSpanHandler#onBeforePopup 弹窗处理实现
|
||||
* @see CefRequestHandler#onBeforeBrowse 导航处理实现
|
||||
*/
|
||||
public Builder openLinksInBrowser(boolean openInBrowser) {
|
||||
this.openLinksInExternalBrowser = !openInBrowser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder minimizable(boolean minimizable) {
|
||||
this.minimizable = minimizable;
|
||||
return this;
|
||||
@@ -72,6 +114,15 @@ public class BrowserWindow extends JFrame {
|
||||
this.windowId = windowId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浏览器创建回调
|
||||
* @param callback 回调
|
||||
*/
|
||||
public Builder setBrowserCreationCallback(BrowserCreationCallback callback){
|
||||
this.browserCreationCallback = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浏览器窗口标题
|
||||
* @param title 标题
|
||||
@@ -113,12 +164,14 @@ public class BrowserWindow extends JFrame {
|
||||
* 设置HTML路径
|
||||
*/
|
||||
public BrowserWindow build() {
|
||||
if (this.htmlPath == null || this.htmlPath.isEmpty()) {
|
||||
throw new IllegalArgumentException("HTML paths cannot be empty");
|
||||
}
|
||||
File htmlFile = new File(this.htmlPath);
|
||||
if (!htmlFile.exists()) {
|
||||
throw new RuntimeException("The HTML file does not exist: " + htmlFile.getAbsolutePath());
|
||||
if (htmlUrl.isEmpty()) {
|
||||
if (this.htmlPath == null || this.htmlPath.isEmpty()) {
|
||||
throw new IllegalArgumentException("HTML paths cannot be empty");
|
||||
}
|
||||
File htmlFile = new File(this.htmlPath);
|
||||
if (!htmlFile.exists()) {
|
||||
throw new RuntimeException("The HTML file does not exist: " + htmlFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
return new BrowserWindow(this);
|
||||
}
|
||||
@@ -131,12 +184,22 @@ public class BrowserWindow extends JFrame {
|
||||
this.htmlPath = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Url
|
||||
* @param htmlUrl Url路径
|
||||
*/
|
||||
public Builder htmlUrl(String htmlUrl) {
|
||||
this.htmlUrl = htmlUrl;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private BrowserWindow(Builder builder) {
|
||||
this.windowId = builder.windowId;
|
||||
this.htmlPath = builder.htmlPath;
|
||||
this.operationHandler = builder.operationHandler;
|
||||
this.htmlUrl = builder.htmlUrl;
|
||||
|
||||
// 设置图标(如果存在)
|
||||
if (builder.icon != null) {
|
||||
@@ -230,12 +293,20 @@ public class BrowserWindow extends JFrame {
|
||||
@Override
|
||||
public boolean onBeforePopup(CefBrowser browser, CefFrame frame,
|
||||
String targetUrl, String targetFrameName) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(targetUrl));
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to open external browser: " + e.getMessage());
|
||||
// 处理弹出窗口:根据配置决定打开方式
|
||||
if (builder.openLinksInExternalBrowser) {
|
||||
// 使用默认浏览器打开
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(targetUrl));
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to open external browser: " + e.getMessage());
|
||||
}
|
||||
return true; // 拦截弹窗
|
||||
} else {
|
||||
// 在当前浏览器中打开
|
||||
browser.loadURL(targetUrl);
|
||||
return true; // 拦截弹窗并在当前窗口打开
|
||||
}
|
||||
return true; // 拦截弹窗
|
||||
}
|
||||
});
|
||||
|
||||
@@ -243,12 +314,19 @@ public class BrowserWindow extends JFrame {
|
||||
@Override
|
||||
public boolean onBeforeBrowse(CefBrowser browser, CefFrame frame,
|
||||
CefRequest request, boolean userGesture, boolean isRedirect) {
|
||||
// 处理主窗口导航
|
||||
if (userGesture) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(request.getURL()));
|
||||
return true; // 取消内置浏览器导航
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to open external browser: " + e.getMessage());
|
||||
if (builder.openLinksInExternalBrowser) {
|
||||
// 使用默认浏览器打开
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(request.getURL()));
|
||||
return true; // 取消内置浏览器导航
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to open external browser: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
// 允许在当前浏览器中打开
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -345,14 +423,46 @@ public class BrowserWindow extends JFrame {
|
||||
Thread.currentThread().setName("BrowserRenderThread");
|
||||
|
||||
// 4. 加载HTML
|
||||
String fileUrl = new File(htmlPath).toURI().toURL().toString();
|
||||
System.out.println("Loading HTML from: " + fileUrl);
|
||||
if (htmlUrl.isEmpty()){
|
||||
String fileUrl = new File(htmlPath).toURI().toURL().toString();
|
||||
System.out.println("Loading HTML from: " + fileUrl);
|
||||
|
||||
// 5. 创建浏览器组件(直接添加到内容面板)
|
||||
browser = client.createBrowser(fileUrl, false, false);
|
||||
// 5. 创建浏览器组件(直接添加到内容面板)
|
||||
browser = client.createBrowser(fileUrl, false, false);
|
||||
} else {
|
||||
System.out.println("Loading Url from: " + htmlUrl);
|
||||
browser = client.createBrowser(htmlUrl, false, false);
|
||||
}
|
||||
|
||||
Component browserComponent = browser.getUIComponent();
|
||||
browser.executeJavaScript("console.log('Java -> HTML 消息测试')",null,2);
|
||||
if (builder.browserCreationCallback != null) {
|
||||
boolean handled = builder.browserCreationCallback.onLayoutCustomization(
|
||||
this, // 当前窗口
|
||||
getContentPane(), // 内容面板
|
||||
browserComponent, // 浏览器组件
|
||||
builder // 构建器对象
|
||||
);
|
||||
|
||||
// 如果回调返回true,跳过默认布局
|
||||
if (handled) {
|
||||
// 设置窗口基本属性
|
||||
setTitle(builder.title);
|
||||
setSize(builder.size);
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
|
||||
// 添加资源释放监听器
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosed(WindowEvent e) {
|
||||
browser.close(true);
|
||||
client.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
setVisible(true);
|
||||
return browserComponent; // 直接返回,跳过默认布局
|
||||
}
|
||||
}
|
||||
|
||||
CefMessageRouter.CefMessageRouterConfig config = new CefMessageRouter.CefMessageRouterConfig();
|
||||
config.jsQueryFunction = "javaQuery";// 定义方法
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.axis.innovators.box.browser;
|
||||
|
||||
import com.axis.innovators.box.events.BrowserCreationCallback;
|
||||
import org.cef.CefApp;
|
||||
import org.cef.CefClient;
|
||||
import org.cef.CefSettings;
|
||||
@@ -31,6 +32,7 @@ import static org.cef.callback.CefMenuModel.MenuId.MENU_ID_USER_FIRST;
|
||||
*/
|
||||
public class BrowserWindowJDialog extends JDialog {
|
||||
private final String windowId;
|
||||
private final String htmlUrl;
|
||||
private CefApp cefApp;
|
||||
private CefClient client;
|
||||
private CefBrowser browser;
|
||||
@@ -52,7 +54,9 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
private boolean resizable = true; // 默认允许调整大小
|
||||
private boolean maximizable = true; // 默认允许最大化
|
||||
private boolean minimizable = true; // 默认允许最小化
|
||||
|
||||
private String htmlUrl = "";
|
||||
private BrowserCreationCallback browserCreationCallback;
|
||||
private boolean openLinksInExternalBrowser = true; // 默认使用外部浏览器
|
||||
|
||||
public Builder resizable(boolean resizable) {
|
||||
this.resizable = resizable;
|
||||
@@ -81,6 +85,53 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 设置链接打开方式
|
||||
*
|
||||
* @param openInBrowser 是否在当前浏览器窗口中打开链接
|
||||
* true - 在当前浏览器窗口中打开链接(本地跳转)
|
||||
* false - 使用系统默认浏览器打开链接(外部跳转)
|
||||
* @return Builder实例,支持链式调用
|
||||
*
|
||||
* @apiNote 此方法控制两种不同的链接打开行为:
|
||||
* 1. 当设置为true时:
|
||||
* - 所有链接将在当前CEF浏览器窗口内打开
|
||||
*
|
||||
* 2. 当设置为false时(默认值):
|
||||
* - 所有链接将在系统默认浏览器中打开
|
||||
* - 更安全,避免潜在的安全风险
|
||||
* - 适用于简单的信息展示场景
|
||||
*
|
||||
* @implNote 内部实现说明:
|
||||
* - 实际存储的是反向值(openLinksInExternalBrowser)
|
||||
* - 这样设置是为了保持与历史版本的兼容性
|
||||
* - 方法名使用"openInBrowser"更符合用户直觉
|
||||
*
|
||||
* @example 使用示例:
|
||||
* // 在当前窗口打开链接
|
||||
* new Builder().openLinksInBrowser(true).build();
|
||||
*
|
||||
* // 使用系统浏览器打开链接(默认)
|
||||
* new Builder().openLinksInBrowser(false).build();
|
||||
*
|
||||
* @see #openLinksInExternalBrowser 内部存储字段
|
||||
* @see CefLifeSpanHandler#onBeforePopup 弹窗处理实现
|
||||
* @see CefRequestHandler#onBeforeBrowse 导航处理实现
|
||||
*/
|
||||
public Builder openLinksInBrowser(boolean openInBrowser) {
|
||||
this.openLinksInExternalBrowser = !openInBrowser;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置浏览器创建回调
|
||||
* @param callback 回调
|
||||
*/
|
||||
public Builder setBrowserCreationCallback(BrowserCreationCallback callback){
|
||||
this.browserCreationCallback = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浏览器窗口大小
|
||||
@@ -124,12 +175,14 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
* 设置HTML路径
|
||||
*/
|
||||
public BrowserWindowJDialog build() {
|
||||
if (this.htmlPath == null || this.htmlPath.isEmpty()) {
|
||||
throw new IllegalArgumentException("HTML paths cannot be empty");
|
||||
}
|
||||
File htmlFile = new File(this.htmlPath);
|
||||
if (!htmlFile.exists()) {
|
||||
throw new RuntimeException("The HTML file does not exist: " + htmlFile.getAbsolutePath());
|
||||
if (htmlUrl.isEmpty()) {
|
||||
if (this.htmlPath == null || this.htmlPath.isEmpty()) {
|
||||
throw new IllegalArgumentException("HTML paths cannot be empty");
|
||||
}
|
||||
File htmlFile = new File(this.htmlPath);
|
||||
if (!htmlFile.exists()) {
|
||||
throw new RuntimeException("The HTML file does not exist: " + htmlFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
return new BrowserWindowJDialog(this);
|
||||
}
|
||||
@@ -142,13 +195,24 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
this.htmlPath = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Url
|
||||
* @param htmlUrl Url路径
|
||||
*/
|
||||
public Builder htmlUrl(String htmlUrl) {
|
||||
this.htmlUrl = htmlUrl;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private BrowserWindowJDialog(Builder builder) {
|
||||
// 根据父窗口是否存在,设置是否为模态对话框
|
||||
super(builder.parentFrame, builder.title, builder.parentFrame != null);
|
||||
this.windowId = builder.windowId;
|
||||
this.htmlPath = builder.htmlPath;
|
||||
this.htmlUrl = builder.htmlUrl;
|
||||
this.operationHandler = builder.operationHandler;
|
||||
|
||||
// 设置图标(如果存在)
|
||||
@@ -177,19 +241,13 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
client = cefApp.createClient();
|
||||
client.addDisplayHandler(new CefDisplayHandler (){
|
||||
@Override
|
||||
public void onAddressChange(CefBrowser browser, CefFrame frame, String url) {
|
||||
|
||||
}
|
||||
public void onAddressChange(CefBrowser browser, CefFrame frame, String url) {}
|
||||
|
||||
@Override
|
||||
public void onTitleChange(CefBrowser browser, String title) {
|
||||
|
||||
}
|
||||
public void onTitleChange(CefBrowser browser, String title) {}
|
||||
|
||||
@Override
|
||||
public void OnFullscreenModeChange(CefBrowser browser, boolean fullscreen) {
|
||||
|
||||
}
|
||||
public void OnFullscreenModeChange(CefBrowser browser, boolean fullscreen) {}
|
||||
|
||||
@Override
|
||||
public boolean onTooltip(CefBrowser browser, String text) {
|
||||
@@ -197,9 +255,7 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusMessage(CefBrowser browser, String value) {
|
||||
|
||||
}
|
||||
public void onStatusMessage(CefBrowser browser, String value) {}
|
||||
|
||||
@Override
|
||||
public boolean onConsoleMessage(
|
||||
@@ -242,12 +298,20 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
@Override
|
||||
public boolean onBeforePopup(CefBrowser browser, CefFrame frame,
|
||||
String targetUrl, String targetFrameName) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(targetUrl));
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to open external browser: " + e.getMessage());
|
||||
// 处理弹出窗口:根据配置决定打开方式
|
||||
if (builder.openLinksInExternalBrowser) {
|
||||
// 使用默认浏览器打开
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(targetUrl));
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to open external browser: " + e.getMessage());
|
||||
}
|
||||
return true; // 拦截弹窗
|
||||
} else {
|
||||
// 在当前浏览器中打开
|
||||
browser.loadURL(targetUrl);
|
||||
return true; // 拦截弹窗并在当前窗口打开
|
||||
}
|
||||
return true; // 拦截弹窗
|
||||
}
|
||||
});
|
||||
|
||||
@@ -255,12 +319,19 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
@Override
|
||||
public boolean onBeforeBrowse(CefBrowser browser, CefFrame frame,
|
||||
CefRequest request, boolean userGesture, boolean isRedirect) {
|
||||
// 处理主窗口导航
|
||||
if (userGesture) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(request.getURL()));
|
||||
return true; // 取消内置浏览器导航
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to open external browser: " + e.getMessage());
|
||||
if (builder.openLinksInExternalBrowser) {
|
||||
// 使用默认浏览器打开
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(request.getURL()));
|
||||
return true; // 取消内置浏览器导航
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to open external browser: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
// 允许在当前浏览器中打开
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -357,14 +428,48 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
Thread.currentThread().setName("BrowserRenderThread");
|
||||
|
||||
// 4. 加载HTML
|
||||
String fileUrl = new File(htmlPath).toURI().toURL().toString();
|
||||
System.out.println("Loading HTML from: " + fileUrl);
|
||||
if (htmlUrl.isEmpty()) {
|
||||
String fileUrl = new File(htmlPath).toURI().toURL().toString();
|
||||
System.out.println("Loading HTML from: " + fileUrl);
|
||||
|
||||
// 5. 创建浏览器组件(直接添加到内容面板)
|
||||
browser = client.createBrowser(fileUrl, false, false);
|
||||
// 5. 创建浏览器组件(直接添加到内容面板)
|
||||
browser = client.createBrowser(fileUrl, false, false);
|
||||
} else {
|
||||
System.out.println("Loading HTML from: " + htmlUrl);
|
||||
browser = client.createBrowser(htmlUrl, false, false);
|
||||
}
|
||||
|
||||
Component browserComponent = browser.getUIComponent();
|
||||
browser.executeJavaScript("console.log('Java -> HTML 消息测试')",null,2);
|
||||
|
||||
if (builder.browserCreationCallback != null) {
|
||||
boolean handled = builder.browserCreationCallback.onLayoutCustomization(
|
||||
this, // 当前窗口
|
||||
getContentPane(), // 内容面板
|
||||
browserComponent, // 浏览器组件
|
||||
builder // 构建器对象
|
||||
);
|
||||
|
||||
// 如果回调返回true,跳过默认布局
|
||||
if (handled) {
|
||||
// 设置窗口基本属性
|
||||
setTitle(builder.title);
|
||||
setSize(builder.size);
|
||||
setLocationRelativeTo(builder.parentFrame);
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
|
||||
// 添加资源释放监听器
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosed(WindowEvent e) {
|
||||
browser.close(true);
|
||||
client.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
setVisible(true);
|
||||
return browserComponent; // 直接返回,跳过默认布局
|
||||
}
|
||||
}
|
||||
|
||||
CefMessageRouter.CefMessageRouterConfig config = new CefMessageRouter.CefMessageRouterConfig();
|
||||
config.jsQueryFunction = "javaQuery";// 定义方法
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package com.axis.innovators.box.browser;
|
||||
|
||||
import com.axis.innovators.box.AxisInnovatorsBox;
|
||||
import com.axis.innovators.box.register.LanguageManager;
|
||||
import com.axis.innovators.box.tools.FolderCreator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.cef.CefApp;
|
||||
import org.cef.CefSettings;
|
||||
import org.cef.callback.CefCommandLine;
|
||||
import org.cef.handler.CefAppHandlerAdapter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -56,22 +60,62 @@ public class CefAppManager {
|
||||
initLock.lock();
|
||||
try {
|
||||
settings.windowless_rendering_enabled = false;
|
||||
settings.javascript_flags = "--expose-gc";
|
||||
settings.javascript_flags = "";
|
||||
settings.cache_path = FolderCreator.getLibraryFolder() + "/jcef/cache";
|
||||
settings.root_cache_path = FolderCreator.getLibraryFolder() + "/jcef/cache";
|
||||
settings.persist_session_cookies = true;
|
||||
settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_VERBOSE;
|
||||
settings.persist_session_cookies = false;
|
||||
settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_WARNING;
|
||||
|
||||
String subprocessPath = FolderCreator.getLibraryFolder() + "/jcef/lib/win64/jcef_helper.exe";
|
||||
validateSubprocessPath(subprocessPath);
|
||||
settings.browser_subprocess_path = subprocessPath;
|
||||
|
||||
logger.info("Default CEF settings initialized");
|
||||
//settings.background_color = new Color(255, 255, 255, 0);
|
||||
settings.command_line_args_disabled = false;
|
||||
|
||||
// 转换语言标识格式:system:zh_CN -> zh-CN
|
||||
|
||||
CefApp.addAppHandler(new CefAppHandlerAdapter(null) {
|
||||
@Override
|
||||
public void onBeforeCommandLineProcessing(
|
||||
String processType,
|
||||
CefCommandLine commandLine
|
||||
) {
|
||||
LanguageManager.loadSavedLanguage();
|
||||
LanguageManager.Language currentLang = LanguageManager.getLoadedLanguages();
|
||||
if (currentLang != null){
|
||||
String langCode = currentLang.getRegisteredName()
|
||||
.replace("system:", "")
|
||||
.replace("_", "-")
|
||||
.toLowerCase();
|
||||
settings.locale = langCode;
|
||||
commandLine.appendSwitchWithValue("--lang", langCode);
|
||||
commandLine.appendSwitchWithValue("--accept-language", langCode);
|
||||
}
|
||||
|
||||
boolean isDarkTheme = isDarkTheme();
|
||||
if (isDarkTheme) {
|
||||
commandLine.appendSwitch("force-dark-mode");
|
||||
commandLine.appendSwitchWithValue("enable-features", "WebContentsForceDark");
|
||||
}
|
||||
|
||||
logger.info("CEF commandLine: {}", commandLine.getSwitches());
|
||||
}
|
||||
});
|
||||
|
||||
logger.info("Optimized CEF settings initialized");
|
||||
} finally {
|
||||
initLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDarkTheme() {
|
||||
if (AxisInnovatorsBox.getMain() == null){
|
||||
return false;
|
||||
}
|
||||
return AxisInnovatorsBox.getMain().getRegistrationTopic().isDarkMode();
|
||||
}
|
||||
|
||||
public static CefApp getInstance() {
|
||||
if (cefApp == null) {
|
||||
if (initLock.tryLock()) {
|
||||
|
||||
@@ -37,7 +37,15 @@ public class MainApplication {
|
||||
private static long ctxHandle;
|
||||
private static boolean isSystem = true;
|
||||
public static void main(String[] args) {
|
||||
|
||||
AtomicReference<BrowserWindow> window = new AtomicReference<>();
|
||||
WindowRegistry.getInstance().createNewWindow("main", builder ->
|
||||
window.set(builder.title("Axis Innovators Box AI 工具箱")
|
||||
.size(1280, 720)
|
||||
.htmlUrl("https://chat.deepseek.com/")
|
||||
.openLinksInBrowser(true)
|
||||
.operationHandler(createOperationHandler())
|
||||
.build())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.axis.innovators.box.events;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public interface BrowserCreationCallback {
|
||||
/**
|
||||
* 布局自定义回调方法
|
||||
*
|
||||
* @param window 当前浏览器窗口
|
||||
* @param contentPane 窗口内容面板
|
||||
* @param browserComponent 浏览器UI组件
|
||||
* @param builder 构建器对象
|
||||
* @return 返回true表示已处理布局,将跳过默认布局;返回false将继续执行默认布局
|
||||
*/
|
||||
boolean onLayoutCustomization(
|
||||
Window window,
|
||||
Container contentPane,
|
||||
Component browserComponent,
|
||||
Object builder
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.axis.innovators.box.events;
|
||||
|
||||
import com.axis.innovators.box.gui.MainWindow;
|
||||
import com.axis.innovators.box.window.MainWindow;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.axis.innovators.box.events;
|
||||
|
||||
import com.axis.innovators.box.gui.WindowsJDialog;
|
||||
import com.axis.innovators.box.window.WindowsJDialog;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
|
||||
@@ -1,435 +0,0 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
|
||||
import com.axis.innovators.box.verification.OnlineVerification;
|
||||
import com.axis.innovators.box.verification.UserTags;
|
||||
import com.axis.innovators.box.verification.VerificationService;
|
||||
import com.formdev.flatlaf.FlatDarculaLaf;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.LineBorder;
|
||||
import java.awt.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 用于登录的主窗口
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class LoginWindow {
|
||||
private static final AtomicReference<UserTags> loginResult = new AtomicReference<>(UserTags.None);
|
||||
private JDialog dialog;
|
||||
private JTextField emailField;
|
||||
private JPasswordField passwordField;
|
||||
private JCheckBox showPasswordCheckBox;
|
||||
private static final int FIELD_WIDTH = 300;
|
||||
private static final int FIELD_HEIGHT = 35;
|
||||
public static UserTags createAndShow() throws InterruptedException, InvocationTargetException {
|
||||
AtomicReference<UserTags> result = new AtomicReference<>(UserTags.None);
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
LoginWindow loginWindow = new LoginWindow();
|
||||
loginWindow.dialog.setVisible(true);
|
||||
result.set(loginResult.get());
|
||||
|
||||
if (result.get() == UserTags.None) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
|
||||
private LoginWindow() {
|
||||
setupLookAndFeel();
|
||||
createMainDialog();
|
||||
buildLoginUI();
|
||||
}
|
||||
|
||||
private void setupLookAndFeel() {
|
||||
try {
|
||||
UIManager.setLookAndFeel(new FlatDarculaLaf());
|
||||
// 自定义输入框样式
|
||||
UIManager.put("Component.arc", 12);
|
||||
UIManager.put("TextComponent.arc", 12);
|
||||
UIManager.put("Button.arc", 8);
|
||||
UIManager.put("ProgressBar.arc", 8);
|
||||
UIManager.put("TextComponent.background", new Color(0x383838));
|
||||
UIManager.put("TextField.placeholderForeground", new Color(0x808080));
|
||||
} catch (UnsupportedLookAndFeelException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void createMainDialog() {
|
||||
dialog = new JDialog((Frame) null, "AXIS 安全认证", true);
|
||||
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
dialog.setSize(380, 500);
|
||||
dialog.setLocationRelativeTo(null);
|
||||
dialog.setResizable(false);
|
||||
dialog.setLayout(new BorderLayout());
|
||||
}
|
||||
|
||||
private void buildLoginUI() {
|
||||
JPanel mainPanel = new JPanel();
|
||||
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30));
|
||||
|
||||
JLabel titleLabel = new JLabel("欢迎登录");
|
||||
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
|
||||
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
mainPanel.add(titleLabel);
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
|
||||
|
||||
JPanel inputContainer = new JPanel();
|
||||
inputContainer.setLayout(new BoxLayout(inputContainer, BoxLayout.Y_AXIS));
|
||||
inputContainer.setOpaque(false);
|
||||
inputContainer.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
|
||||
inputContainer.add(createInputField("账号", buildEmailField()));
|
||||
inputContainer.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
|
||||
inputContainer.add(createInputField("密码", buildPasswordField()));
|
||||
|
||||
mainPanel.add(inputContainer);
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
|
||||
|
||||
JButton loginButton = createLoginButton();
|
||||
loginButton.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
mainPanel.add(loginButton);
|
||||
|
||||
JPanel linksPanel = createUtilityLinks();
|
||||
linksPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
|
||||
mainPanel.add(linksPanel);
|
||||
|
||||
mainPanel.add(Box.createVerticalGlue());
|
||||
|
||||
dialog.add(mainPanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
private JComponent buildEmailField() {
|
||||
emailField = new JTextField();
|
||||
customizeTextField(emailField, "请输入邮箱或手机号");
|
||||
return emailField;
|
||||
}
|
||||
|
||||
private JComponent buildPasswordField() {
|
||||
JPanel passwordPanel = new JPanel(new BorderLayout());
|
||||
passwordPanel.setOpaque(false);
|
||||
passwordPanel.setBorder(new RoundBorder(8, new Color(0x454545)));
|
||||
|
||||
// 密码输入框
|
||||
passwordField = new JPasswordField();
|
||||
passwordField.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
||||
passwordField.putClientProperty("JTextField.placeholderText", "请输入密码");
|
||||
passwordField.setMaximumSize(new Dimension(FIELD_WIDTH, FIELD_HEIGHT));
|
||||
passwordField.setPreferredSize(new Dimension(FIELD_WIDTH, FIELD_HEIGHT));
|
||||
passwordField.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 5));
|
||||
|
||||
passwordPanel.add(passwordField, BorderLayout.CENTER);
|
||||
|
||||
// 显示密码按钮
|
||||
JPanel togglePanel = new JPanel(new BorderLayout());
|
||||
togglePanel.setOpaque(false);
|
||||
togglePanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
|
||||
|
||||
showPasswordCheckBox = new JCheckBox();
|
||||
showPasswordCheckBox.setOpaque(false);
|
||||
showPasswordCheckBox.setPreferredSize(new Dimension(32, 32));
|
||||
showPasswordCheckBox.setBackground(new Color(0x46494B));
|
||||
showPasswordCheckBox.setIcon(UIManager.getIcon("PasswordView.icon"));
|
||||
showPasswordCheckBox.setSelectedIcon(UIManager.getIcon("PasswordHide.icon"));
|
||||
showPasswordCheckBox.addActionListener(e -> togglePasswordVisibility());
|
||||
togglePanel.add(showPasswordCheckBox, BorderLayout.CENTER);
|
||||
|
||||
passwordPanel.add(togglePanel, BorderLayout.EAST);
|
||||
return passwordPanel;
|
||||
}
|
||||
|
||||
private void customizeTextField(JComponent field, String placeholder) {
|
||||
field.setPreferredSize(new Dimension(FIELD_WIDTH, FIELD_HEIGHT));
|
||||
field.setMaximumSize(new Dimension(FIELD_WIDTH, FIELD_HEIGHT));
|
||||
field.putClientProperty("JComponent.roundRect", true);
|
||||
field.putClientProperty("JTextField.placeholderText", placeholder);
|
||||
field.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
||||
field.setBorder(BorderFactory.createCompoundBorder(
|
||||
new RoundBorder(8, new Color(0x454545)),
|
||||
BorderFactory.createEmptyBorder(10, 15, 10, 15)
|
||||
));
|
||||
}
|
||||
|
||||
private JPanel createInputField(String label, JComponent field) {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
||||
panel.setOpaque(false);
|
||||
panel.setMaximumSize(new Dimension(280, 62));
|
||||
|
||||
JLabel titleLabel = new JLabel(label);
|
||||
titleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
|
||||
titleLabel.setForeground(new Color(0x9E9E9E));
|
||||
titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
|
||||
panel.add(titleLabel);
|
||||
panel.add(Box.createVerticalStrut(8));
|
||||
panel.add(field);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JButton createLoginButton() {
|
||||
JButton button = new JButton("立即登录");
|
||||
button.setFont(new Font("微软雅黑", Font.BOLD, 14));
|
||||
button.setForeground(Color.WHITE);
|
||||
button.setBackground(new Color(0x2B579A));
|
||||
|
||||
button.setPreferredSize(new Dimension(285, 40));
|
||||
button.setMaximumSize(new Dimension(285, 40));
|
||||
|
||||
button.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
|
||||
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
button.addActionListener(e -> attemptLogin());
|
||||
|
||||
// 悬停效果
|
||||
button.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseEntered(java.awt.event.MouseEvent evt) {
|
||||
button.setBackground(new Color(0x3C6DB0));
|
||||
}
|
||||
|
||||
public void mouseExited(java.awt.event.MouseEvent evt) {
|
||||
button.setBackground(new Color(0x2B579A));
|
||||
}
|
||||
});
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private static class RoundBorder extends LineBorder {
|
||||
private final int radius;
|
||||
|
||||
public RoundBorder(int radius, Color color) {
|
||||
super(color, 1);
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2.drawRoundRect(x, y, width-1, height-1, radius, radius);
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private JPanel createUtilityLinks() {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
|
||||
panel.setOpaque(false);
|
||||
|
||||
JButton registerBtn = createTextButton("注册账号", this::showRegister);
|
||||
JButton forgotBtn = createTextButton("忘记密码", this::showForgotPassword);
|
||||
|
||||
panel.add(Box.createHorizontalGlue());
|
||||
panel.add(registerBtn);
|
||||
panel.add(Box.createHorizontalStrut(15));
|
||||
panel.add(forgotBtn);
|
||||
panel.add(Box.createHorizontalGlue());
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JButton createTextButton(String text, Runnable action) {
|
||||
JButton button = new JButton(text);
|
||||
button.setForeground(new Color(0x8AB4F8));
|
||||
button.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
||||
button.setContentAreaFilled(false);
|
||||
button.setBorderPainted(false);
|
||||
button.setFocusPainted(false);
|
||||
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
button.addActionListener(e -> action.run());
|
||||
return button;
|
||||
}
|
||||
|
||||
private void togglePasswordVisibility() {
|
||||
if (showPasswordCheckBox.isSelected()) {
|
||||
passwordField.setEchoChar((char) 0);
|
||||
} else {
|
||||
passwordField.setEchoChar('•');
|
||||
}
|
||||
}
|
||||
|
||||
private void attemptLogin() {
|
||||
String email = emailField.getText().trim();
|
||||
String password = new String(passwordField.getPassword()).trim();
|
||||
|
||||
if (email.isEmpty() || password.isEmpty()) {
|
||||
showError("请输入完整的登录信息");
|
||||
return;
|
||||
}
|
||||
|
||||
OnlineVerification onlineVerification = OnlineVerification.validateLogin(email, password);
|
||||
loginResult.set(VerificationService.determineUserType(onlineVerification));
|
||||
|
||||
dialog.dispose();
|
||||
}
|
||||
|
||||
private void showError(String message) {
|
||||
JOptionPane.showMessageDialog(dialog, message, "验证错误",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
private void showRegister() {
|
||||
JDialog registerDialog = new JDialog(dialog, "注册新账号", true);
|
||||
registerDialog.setSize(380, 500);
|
||||
registerDialog.setLocationRelativeTo(dialog);
|
||||
registerDialog.setResizable(false);
|
||||
registerDialog.setLayout(new BorderLayout());
|
||||
|
||||
JPanel mainPanel = new JPanel();
|
||||
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30));
|
||||
|
||||
// 标题
|
||||
JLabel titleLabel = new JLabel("新用户注册");
|
||||
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
|
||||
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
mainPanel.add(titleLabel);
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
|
||||
|
||||
// 输入容器
|
||||
JPanel inputContainer = new JPanel();
|
||||
inputContainer.setLayout(new BoxLayout(inputContainer, BoxLayout.Y_AXIS));
|
||||
inputContainer.setOpaque(false);
|
||||
|
||||
// 用户名
|
||||
JTextField usernameField = new JTextField();
|
||||
customizeTextField(usernameField, "请输入用户名");
|
||||
inputContainer.add(createInputField("用户名", usernameField));
|
||||
inputContainer.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
|
||||
// 邮箱
|
||||
JTextField emailField = new JTextField();
|
||||
customizeTextField(emailField, "请输入邮箱");
|
||||
inputContainer.add(createInputField("邮箱", emailField));
|
||||
inputContainer.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
|
||||
// 密码
|
||||
JPasswordField passwordField = new JPasswordField();
|
||||
customizeTextField(passwordField, "请输入密码");
|
||||
inputContainer.add(createInputField("密码", passwordField));
|
||||
inputContainer.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
|
||||
// 确认密码
|
||||
JPasswordField confirmField = new JPasswordField();
|
||||
customizeTextField(confirmField, "请再次输入密码");
|
||||
inputContainer.add(createInputField("确认密码", confirmField));
|
||||
|
||||
mainPanel.add(inputContainer);
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
|
||||
|
||||
// 注册按钮
|
||||
JButton regButton = new JButton("立即注册");
|
||||
styleButton(regButton);
|
||||
regButton.addActionListener(e -> {
|
||||
String pwd = new String(passwordField.getPassword());
|
||||
String confirm = new String(confirmField.getPassword());
|
||||
if(!pwd.equals(confirm)) {
|
||||
JOptionPane.showMessageDialog(registerDialog, "两次输入的密码不一致", "注册错误",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
// 调用验证服务
|
||||
boolean success = VerificationService.registerUser(
|
||||
usernameField.getText(),
|
||||
emailField.getText(),
|
||||
pwd
|
||||
);
|
||||
if(success) {
|
||||
JOptionPane.showMessageDialog(registerDialog, "注册成功,请登录", "注册成功",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
registerDialog.dispose();
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(registerDialog, "注册失败,请检查信息", "注册错误",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
});
|
||||
mainPanel.add(regButton);
|
||||
|
||||
// 返回登录链接
|
||||
JButton backBtn = createTextButton("返回登录", registerDialog::dispose);
|
||||
backBtn.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 15)));
|
||||
mainPanel.add(backBtn);
|
||||
|
||||
registerDialog.add(mainPanel, BorderLayout.CENTER);
|
||||
registerDialog.setVisible(true);
|
||||
}
|
||||
|
||||
private void showForgotPassword() {
|
||||
JDialog forgotDialog = new JDialog(dialog, "找回密码", true);
|
||||
forgotDialog.setSize(380, 350);
|
||||
forgotDialog.setLocationRelativeTo(dialog);
|
||||
forgotDialog.setResizable(false);
|
||||
forgotDialog.setLayout(new BorderLayout());
|
||||
|
||||
JPanel mainPanel = new JPanel();
|
||||
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30));
|
||||
|
||||
JLabel titleLabel = new JLabel("找回密码");
|
||||
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
|
||||
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
mainPanel.add(titleLabel);
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
|
||||
|
||||
// 邮箱输入
|
||||
JTextField emailField = new JTextField();
|
||||
customizeTextField(emailField, "请输入注册邮箱");
|
||||
mainPanel.add(createInputField("注册邮箱", emailField));
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
|
||||
|
||||
// 提交按钮
|
||||
JButton submitBtn = new JButton("发送重置邮件");
|
||||
styleButton(submitBtn);
|
||||
submitBtn.addActionListener(e -> {
|
||||
if(VerificationService.sendPasswordReset(emailField.getText())) {
|
||||
JOptionPane.showMessageDialog(forgotDialog, "重置邮件已发送,请查收", "操作成功",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
forgotDialog.dispose();
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(forgotDialog, "邮箱未注册或发送失败", "操作失败",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
});
|
||||
mainPanel.add(submitBtn);
|
||||
|
||||
// 返回链接
|
||||
JButton backBtn = createTextButton("返回登录", forgotDialog::dispose);
|
||||
backBtn.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 15)));
|
||||
mainPanel.add(backBtn);
|
||||
|
||||
forgotDialog.add(mainPanel, BorderLayout.CENTER);
|
||||
forgotDialog.setVisible(true);
|
||||
}
|
||||
|
||||
// 通用按钮样式方法
|
||||
private void styleButton(JButton button) {
|
||||
button.setFont(new Font("微软雅黑", Font.BOLD, 14));
|
||||
button.setForeground(Color.WHITE);
|
||||
button.setBackground(new Color(0x2B579A));
|
||||
button.setPreferredSize(new Dimension(285, 40));
|
||||
button.setMaximumSize(new Dimension(285, 40));
|
||||
button.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
|
||||
// 悬停效果
|
||||
button.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseEntered(java.awt.event.MouseEvent evt) {
|
||||
button.setBackground(new Color(0x3C6DB0));
|
||||
}
|
||||
public void mouseExited(java.awt.event.MouseEvent evt) {
|
||||
button.setBackground(new Color(0x2B579A));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,818 +0,0 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
|
||||
import com.axis.innovators.box.AxisInnovatorsBox;
|
||||
import com.axis.innovators.box.events.*;
|
||||
import com.axis.innovators.box.register.LanguageManager;
|
||||
import com.axis.innovators.box.register.RegistrationSettingsItem;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.FontUIResource;
|
||||
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.RoundRectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ConvolveOp;
|
||||
import java.awt.image.Kernel;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 显示窗口(显示的主窗口)
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class MainWindow extends JFrame {
|
||||
private static final Logger logger = LogManager.getLogger(MainWindow.class);
|
||||
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 SystemTray systemTray;
|
||||
//private TrayIcon trayIcon;
|
||||
|
||||
public MainWindow() {
|
||||
setIconImage(LoadIcon.loadIcon("logo.png", 32).getImage());
|
||||
addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
for (JComponent card : cardScales.keySet()) {
|
||||
cardScales.put(card, 1.0f);
|
||||
card.repaint();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加工具分类
|
||||
* @param category 工具分类
|
||||
*/
|
||||
public void addToolCategory(ToolCategory category){
|
||||
categories.add(category);
|
||||
}
|
||||
|
||||
public void initUI() {
|
||||
setTitle(LanguageManager.getLoadedLanguages().getText("mainWindow.title"));
|
||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
setSize(1200, 800);
|
||||
setLocationRelativeTo(null);
|
||||
|
||||
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
|
||||
|
||||
getContentPane().setBackground(new Color(0, 0, 0, 0));
|
||||
|
||||
JPanel mainPanel = new JPanel();
|
||||
|
||||
mainPanel.setOpaque(true);
|
||||
|
||||
mainPanel.setLayout(new BorderLayout(20, 20));
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 30, 30, 30));
|
||||
|
||||
mainPanel.add(createHeader(), BorderLayout.NORTH);
|
||||
mainPanel.add(createCategoryTabs(), BorderLayout.CENTER);
|
||||
//mainPanel.add(createFooter(), BorderLayout.SOUTH); 底部菜单
|
||||
|
||||
add(mainPanel);
|
||||
//createSystemTray();
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
setVisible(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private JPanel createFooter() {
|
||||
JPanel footer = new JPanel();
|
||||
footer.setLayout(new BoxLayout(footer, BoxLayout.X_AXIS));
|
||||
footer.setBorder(BorderFactory.createEmptyBorder(15, 30, 15, 30));
|
||||
|
||||
JLabel version = new JLabel("轴创工具箱 v1.0");
|
||||
version.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
||||
version.setForeground(new Color(120, 120, 120));
|
||||
|
||||
footer.add(version);
|
||||
footer.add(Box.createHorizontalGlue());
|
||||
|
||||
JLabel status = new JLabel("已加载 " + categories.size() + " 个分类, " +
|
||||
categories.stream().mapToInt(c -> c.getTools().size()).sum() + " 个工具");
|
||||
status.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
||||
status.setForeground(new Color(120, 120, 120));
|
||||
footer.add(status);
|
||||
|
||||
return footer;
|
||||
}
|
||||
|
||||
// 基础菜单项创建方法(解决方法不存在问题)
|
||||
private MenuItem createBaseMenuItem(String text, ActionListener action) {
|
||||
MenuItem item = new MenuItem(text);
|
||||
item.addActionListener(action);
|
||||
return item;
|
||||
}
|
||||
|
||||
// 圆角图标生成(修正版)
|
||||
private Image createRoundedIcon(Image source, int cornerRadius) {
|
||||
int size = Math.max(source.getWidth(null), source.getHeight(null));
|
||||
BufferedImage output = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
Graphics2D g2 = output.createGraphics();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// 创建剪切路径
|
||||
RoundRectangle2D roundRect = new RoundRectangle2D.Float(0, 0, size, size, cornerRadius, cornerRadius);
|
||||
g2.setClip(roundRect);
|
||||
g2.drawImage(source, 0, 0, size, size, null);
|
||||
|
||||
g2.dispose();
|
||||
return output;
|
||||
}
|
||||
|
||||
// 添加事件监听
|
||||
//private void addTrayEventListeners() {
|
||||
// // 双击恢复窗口
|
||||
// trayIcon.addActionListener(e -> setVisible(true));
|
||||
//
|
||||
// // 右键菜单触发兼容处理
|
||||
// trayIcon.addMouseListener(new MouseAdapter() {
|
||||
// @Override
|
||||
// public void mouseReleased(MouseEvent e) {
|
||||
// if (e.isPopupTrigger()) {
|
||||
// trayIcon.getPopupMenu().show(e.getComponent(), e.getX(), e.getY());
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//}
|
||||
|
||||
// 初始化中文字体(在main方法或构造函数中调用)
|
||||
private void initChineseFont() {
|
||||
String[] fontPriority = {
|
||||
"Microsoft YaHei",
|
||||
"PingFang SC",
|
||||
"SimSun",
|
||||
Font.DIALOG
|
||||
};
|
||||
|
||||
for (String fontName : fontPriority) {
|
||||
if (isFontAvailable(fontName)) {
|
||||
setGlobalFont(new Font(fontName, Font.PLAIN, 12));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 字体可用性检查
|
||||
private boolean isFontAvailable(String fontName) {
|
||||
return Arrays.asList(GraphicsEnvironment
|
||||
.getLocalGraphicsEnvironment()
|
||||
.getAvailableFontFamilyNames())
|
||||
.contains(fontName);
|
||||
}
|
||||
|
||||
// 全局字体设置
|
||||
private void setGlobalFont(Font font) {
|
||||
FontUIResource fontRes = new FontUIResource(font);
|
||||
UIManager.put("MenuItem.font", fontRes);
|
||||
UIManager.put("Menu.font", fontRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对图像应用动感模糊效果。
|
||||
*
|
||||
* @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.putClientProperty(FlatClientProperties.TABBED_PANE_TAB_AREA_ALIGN, Component.LEFT_ALIGNMENT);
|
||||
tabbedPane.putClientProperty(FlatClientProperties.TABBED_PANE_TAB_HEIGHT, 40);
|
||||
tabbedPane.putClientProperty(FlatClientProperties.TABBED_PANE_TAB_ICON_PLACEMENT, SwingConstants.TOP);
|
||||
|
||||
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));
|
||||
|
||||
if (category.getIcon() == null){
|
||||
tabbedPane.addTab(
|
||||
category.getName(),
|
||||
category.getIconImage(),
|
||||
scrollPane
|
||||
);
|
||||
}
|
||||
tabbedPane.addTab(
|
||||
category.getName(),
|
||||
LoadIcon.loadIcon(category.getIcon(), 12),
|
||||
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(LanguageManager.getLoadedLanguages().getText("mainWindow.title.2"));
|
||||
title.setFont(new Font("微软雅黑", Font.BOLD, 28));
|
||||
title.setForeground(new Color(255, 255, 255));
|
||||
|
||||
|
||||
|
||||
JButton settings = new JButton(LoadIcon.loadIcon("settings.png", 32));
|
||||
settings.putClientProperty(FlatClientProperties.BUTTON_TYPE, FlatClientProperties.BUTTON_TYPE_BORDERLESS);
|
||||
settings.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
settings.setContentAreaFilled(false);
|
||||
settings.addActionListener(e -> showSettings());
|
||||
|
||||
JPanel titlePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
titlePanel.setOpaque(false);
|
||||
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 WindowsJDialog dialog;
|
||||
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(600, 400);
|
||||
dialog.setLocationRelativeTo(this);
|
||||
|
||||
JPanel content = new JPanel(new BorderLayout());
|
||||
content.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
|
||||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
|
||||
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);
|
||||
dialog.revalidate();
|
||||
if (AxisInnovatorsBox.getMain().isWindowStartup(dialog)) {
|
||||
AxisInnovatorsBox.getMain().popupWindow(dialog);
|
||||
}
|
||||
}
|
||||
|
||||
private JPanel createToolCard(ToolItem tool) {
|
||||
JPanel card = new JPanel() {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
Graphics2D g2d = (Graphics2D) g.create();
|
||||
|
||||
// 毛玻璃效果
|
||||
BufferedImage background = new BufferedImage(
|
||||
getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
super.paintComponent(background.getGraphics());
|
||||
BufferedImage blurred = applyGaussianBlur(background, 5);
|
||||
|
||||
g2d.drawImage(blurred, 0, 0, null);
|
||||
|
||||
// 绘制阴影
|
||||
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.setPreferredSize(new Dimension(200, 150));
|
||||
card.setMinimumSize(new Dimension(100, 75));
|
||||
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;
|
||||
if (tool.getIcon() == null){
|
||||
iconLabel = new JLabel(tool.getImageIcon());
|
||||
}else {
|
||||
iconLabel = new JLabel(LoadIcon.loadIcon(tool.getIcon(), 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.getTitle());
|
||||
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));
|
||||
titleLabel.setForeground(new Color(255, 255, 255));
|
||||
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
|
||||
JTextArea descArea = new JTextArea(tool.getDescription());
|
||||
descArea.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
||||
descArea.setForeground(new Color(177, 177, 177));
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
card.addMouseListener(new CardMouseAdapter(card, tool) {
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
cardElevations.put(card, 8);
|
||||
animateCardElevation(card, 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
animateCardElevation(card, 2);
|
||||
}
|
||||
});
|
||||
return card;
|
||||
}
|
||||
|
||||
|
||||
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.getDescription() + "</p>" +
|
||||
"</body></html>";
|
||||
}
|
||||
|
||||
private void animateCardElevation(JComponent card, int targetElevation) {
|
||||
new Timer(10, new AbstractAction() {
|
||||
private int currentElevation = cardElevations.getOrDefault(card, 2);
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (currentElevation < targetElevation) currentElevation++;
|
||||
else if (currentElevation > targetElevation) currentElevation--;
|
||||
|
||||
cardElevations.put(card, currentElevation);
|
||||
card.repaint();
|
||||
|
||||
if (currentElevation == targetElevation) {
|
||||
((Timer)e.getSource()).stop();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static class ToolCategory {
|
||||
private final String name;
|
||||
private final String icon;
|
||||
private final ImageIcon iconImage;
|
||||
private final String description;
|
||||
private final UUID id = UUID.randomUUID();
|
||||
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;
|
||||
this.iconImage = null;
|
||||
}
|
||||
|
||||
public ToolCategory(String name, ImageIcon icon, String description) {
|
||||
this.name = name;
|
||||
this.iconImage = icon;
|
||||
this.description = description;
|
||||
this.icon = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册工具的方法
|
||||
* @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;
|
||||
}
|
||||
|
||||
public ImageIcon getIconImage() {
|
||||
return iconImage;
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
// 工具项数据类
|
||||
|
||||
/**
|
||||
* 工具注册类
|
||||
*/
|
||||
public static class ToolItem {
|
||||
private final ImageIcon imageIcon;
|
||||
private final String title;
|
||||
private final String icon;
|
||||
|
||||
private final String description;
|
||||
private final int id;
|
||||
private final Action action;
|
||||
|
||||
public ToolItem(String title, String icon, String description, int id, Action action){
|
||||
this.title = title;
|
||||
this.icon = icon;
|
||||
this.description = description;
|
||||
this.id = id;
|
||||
this.action = action;
|
||||
this.imageIcon = null;
|
||||
}
|
||||
|
||||
public ToolItem(String title, ImageIcon icon, String description, int id, Action action) {
|
||||
this.title = title;
|
||||
this.imageIcon = icon;
|
||||
this.description = description;
|
||||
this.id = id;
|
||||
this.action = action;
|
||||
this.icon = null;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public ImageIcon icon() {
|
||||
return imageIcon;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public ImageIcon getImageIcon() {
|
||||
return imageIcon;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
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;
|
||||
private static int delay = 0;
|
||||
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) {
|
||||
if (delay == 0) {
|
||||
delay++;
|
||||
startReleaseAnimation(() -> tool.getAction().actionPerformed(
|
||||
new ActionEvent(card, ActionEvent.ACTION_PERFORMED, "")
|
||||
));
|
||||
} else {
|
||||
delay--;
|
||||
}
|
||||
}
|
||||
|
||||
@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 configureScrollBarColors() {
|
||||
this.thumbColor = new Color(90, 90, 120);
|
||||
this.trackColor = new Color(50, 50, 70);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JButton createDecreaseButton(int orientation) {
|
||||
return createInvisibleButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JButton createIncreaseButton(int orientation) {
|
||||
return createInvisibleButton();
|
||||
}
|
||||
|
||||
private JButton createInvisibleButton() {
|
||||
JButton btn = new JButton();
|
||||
btn.setPreferredSize(new Dimension(0, 0));
|
||||
btn.setMinimumSize(new Dimension(0, 0));
|
||||
btn.setMaximumSize(new Dimension(0, 0));
|
||||
return btn;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
|
||||
Graphics2D g2 = (Graphics2D)g;
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2.setColor(thumbColor);
|
||||
g2.fillRoundRect(
|
||||
thumbBounds.x + 2,
|
||||
thumbBounds.y + 2,
|
||||
thumbBounds.width - 4,
|
||||
thumbBounds.height - 4,
|
||||
8, 8
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 启动窗口的任务系统
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class ProgressBarManager extends WindowsJDialog {
|
||||
private JFrame loadingFrame;
|
||||
private JProgressBar mainProgressBar;
|
||||
private JProgressBar subProgressBar;
|
||||
private JLabel statusLabel;
|
||||
private JLabel timeLabel;
|
||||
private long startTime;
|
||||
|
||||
private int totalTasks;
|
||||
private int completedTasks;
|
||||
private Map<String, Integer> subTasks = new HashMap<>();
|
||||
|
||||
public ProgressBarManager(String title, int totalTasks) {
|
||||
this.totalTasks = totalTasks;
|
||||
this.completedTasks = 0;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
|
||||
loadingFrame = new JFrame(title);
|
||||
loadingFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||
loadingFrame.setSize(400, 250);
|
||||
loadingFrame.setLocationRelativeTo(null);
|
||||
loadingFrame.setIconImage(LoadIcon.loadIcon("logo.png", 64).getImage());
|
||||
|
||||
JPanel loadingPanel = new JPanel(new BorderLayout());
|
||||
|
||||
mainProgressBar = new JProgressBar(0, 100);
|
||||
mainProgressBar.setStringPainted(true);
|
||||
|
||||
subProgressBar = new JProgressBar(0, 100);
|
||||
subProgressBar.setStringPainted(true);
|
||||
subProgressBar.setString("Subtask Progress");
|
||||
|
||||
statusLabel = new JLabel("Initializing...", SwingConstants.CENTER);
|
||||
timeLabel = new JLabel("Time elapsed: 0s", SwingConstants.CENTER);
|
||||
|
||||
JLabel logoLabel = new JLabel(LoadIcon.loadIcon("logo.png", 64));
|
||||
|
||||
JPanel progressPanel = new JPanel(new GridLayout(2, 1));
|
||||
progressPanel.add(mainProgressBar);
|
||||
progressPanel.add(subProgressBar);
|
||||
|
||||
loadingPanel.add(logoLabel, BorderLayout.NORTH);
|
||||
loadingPanel.add(progressPanel, BorderLayout.CENTER);
|
||||
loadingPanel.add(statusLabel, BorderLayout.SOUTH);
|
||||
loadingPanel.add(timeLabel, BorderLayout.SOUTH);
|
||||
|
||||
loadingFrame.add(loadingPanel);
|
||||
loadingFrame.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新主任务进度
|
||||
* @param completedTasks 已完成的主任务数量
|
||||
*/
|
||||
public void updateMainProgress(int completedTasks) {
|
||||
this.completedTasks = completedTasks;
|
||||
int progress = (int) ((completedTasks / (double) totalTasks) * 100);
|
||||
mainProgressBar.setValue(progress);
|
||||
statusLabel.setText("Main Progress: " + progress + "%");
|
||||
updateTimeLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新子任务进度
|
||||
* @param subTaskName 子任务名称
|
||||
* @param subTaskCompleted 已完成的子任务数量
|
||||
* @param subTaskTotal 子任务总数
|
||||
*/
|
||||
public void updateSubProgress(String subTaskName, int subTaskCompleted, int subTaskTotal) {
|
||||
subTasks.put(subTaskName, subTaskCompleted);
|
||||
int progress = (int) ((subTaskCompleted / (double) subTaskTotal) * 100);
|
||||
subProgressBar.setValue(progress);
|
||||
subProgressBar.setString(subTaskName + ": " + progress + "%");
|
||||
updateTimeLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新总任务数
|
||||
*/
|
||||
public void setTotalTasks(int totalTasks) {
|
||||
this.totalTasks = totalTasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭加载窗口
|
||||
*/
|
||||
public void close() {
|
||||
loadingFrame.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新时间标签
|
||||
*/
|
||||
private void updateTimeLabel() {
|
||||
long elapsedTime = (System.currentTimeMillis() - startTime) / 1000;
|
||||
timeLabel.setText("Time elapsed: " + elapsedTime + "s");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.axis.innovators.box.python;
|
||||
|
||||
import com.axis.innovators.box.AxisInnovatorsBox;
|
||||
import com.axis.innovators.box.gui.MainWindow;
|
||||
import com.axis.innovators.box.window.MainWindow;
|
||||
import com.axis.innovators.box.register.LanguageManager;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
@@ -2,8 +2,8 @@ package com.axis.innovators.box.register;
|
||||
|
||||
|
||||
import com.axis.innovators.box.AxisInnovatorsBox;
|
||||
import com.axis.innovators.box.gui.LoadIcon;
|
||||
import com.axis.innovators.box.gui.WindowsJDialog;
|
||||
import com.axis.innovators.box.window.LoadIcon;
|
||||
import com.axis.innovators.box.window.WindowsJDialog;
|
||||
import com.axis.innovators.box.plugins.PluginDescriptor;
|
||||
import com.axis.innovators.box.plugins.PluginLoader;
|
||||
import com.axis.innovators.box.plugins.PluginPyLoader;
|
||||
@@ -258,6 +258,7 @@ public class RegistrationSettingsItem extends WindowsJDialog {
|
||||
language.getText("settings.4.load_theme_success.3"), JOptionPane.INFORMATION_MESSAGE);
|
||||
registrationTopic.setLoading(registeredName);
|
||||
themeList.repaint();
|
||||
AxisInnovatorsBox.getMain().reloadAllWindow();
|
||||
} catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(null, language.getText("settings.4.load_theme_error")
|
||||
+ e.getMessage(),
|
||||
|
||||
@@ -2,9 +2,9 @@ package com.axis.innovators.box.register;
|
||||
|
||||
import com.axis.innovators.box.AxisInnovatorsBox;
|
||||
import com.axis.innovators.box.browser.MainApplication;
|
||||
import com.axis.innovators.box.gui.FridaWindow;
|
||||
import com.axis.innovators.box.gui.JarApiProfilingWindow;
|
||||
import com.axis.innovators.box.gui.MainWindow;
|
||||
import com.axis.innovators.box.window.FridaWindow;
|
||||
import com.axis.innovators.box.window.JarApiProfilingWindow;
|
||||
import com.axis.innovators.box.window.MainWindow;
|
||||
import com.axis.innovators.box.plugins.PluginDescriptor;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -28,6 +28,7 @@ public class RegistrationTopic {
|
||||
private final List<LookAndFeel> topicsClassesLookAndFeel = new ArrayList<>();
|
||||
private final List<String> topicsName = new ArrayList<>();
|
||||
private final List<String> registeredNameList = new ArrayList<>();
|
||||
private final List<Boolean> isDarkModeList = new ArrayList<>();
|
||||
private final List<Icon> iconList = new ArrayList<>();
|
||||
private final List<String> tipList = new ArrayList<>();
|
||||
private final AxisInnovatorsBox axisInnovatorsBox;
|
||||
@@ -52,7 +53,7 @@ public class RegistrationTopic {
|
||||
* @param registeredName 主题注册名
|
||||
* @throws RegistrationError 如果注册名已存在,则抛出异常
|
||||
*/
|
||||
public void addTopic(String topicClass, String topicName, String tip ,Icon icon, String registeredName) {
|
||||
public void addTopic(String topicClass, String topicName, String tip ,Icon icon, String registeredName, boolean isDarkMode) {
|
||||
if (!axisInnovatorsBox.isWindow()) {
|
||||
if (registeredNameList.contains(registeredName)) {
|
||||
throw new RegistrationError(registeredName + " duplicate registered names");
|
||||
@@ -63,6 +64,7 @@ public class RegistrationTopic {
|
||||
tipList.add(tip);
|
||||
iconList.add(icon);
|
||||
registeredNameList.add(registeredName);
|
||||
isDarkModeList.add(isDarkMode);
|
||||
} else {
|
||||
logger.warn("Wrong time to add topics");
|
||||
}
|
||||
@@ -78,7 +80,7 @@ public class RegistrationTopic {
|
||||
* @param registeredName 主题注册名
|
||||
* @throws RegistrationError 如果注册名已存在,则抛出异常
|
||||
*/
|
||||
public void addTopic(LookAndFeel topicClass, String topicName, String tip ,Icon icon, String registeredName) {
|
||||
public void addTopic(LookAndFeel topicClass, String topicName, String tip ,Icon icon, String registeredName, boolean isDarkMode) {
|
||||
if (!axisInnovatorsBox.isWindow()) {
|
||||
if (registeredNameList.contains(registeredName)) {
|
||||
throw new RegistrationError(registeredName + " duplicate registered names");
|
||||
@@ -89,6 +91,7 @@ public class RegistrationTopic {
|
||||
tipList.add(tip);
|
||||
iconList.add(icon);
|
||||
registeredNameList.add(registeredName);
|
||||
isDarkModeList.add(isDarkMode);
|
||||
} else {
|
||||
logger.warn("Wrong time to add topics");
|
||||
}
|
||||
@@ -107,6 +110,23 @@ public class RegistrationTopic {
|
||||
return Objects.equals(loading, loadTopics);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前加载的主题是否是暗黑模式
|
||||
* @return true 如果是暗黑模式,false 如果是浅色模式
|
||||
*/
|
||||
public boolean isDarkMode() {
|
||||
if (loadTopics.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = registeredNameList.indexOf(loadTopics);
|
||||
if (index >= 0 && index < isDarkModeList.size()) {
|
||||
return isDarkModeList.get(index);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前加载的主题。
|
||||
*
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,34 +130,42 @@ public class StateManager {
|
||||
}
|
||||
|
||||
public int getStateAsInt(String key) {
|
||||
return Integer.parseInt(configMap.get(key));
|
||||
String value = configMap.get(key);
|
||||
return value != null ? Integer.parseInt(value) : 0;
|
||||
}
|
||||
|
||||
public long getStateAsLong(String key) {
|
||||
return Long.parseLong(configMap.get(key));
|
||||
String value = configMap.get(key);
|
||||
return value != null ? Long.parseLong(value) : 0L;
|
||||
}
|
||||
|
||||
public float getStateAsFloat(String key) {
|
||||
return Float.parseFloat(configMap.get(key));
|
||||
String value = configMap.get(key);
|
||||
return value != null ? Float.parseFloat(value) : 0.0f;
|
||||
}
|
||||
|
||||
public boolean getStateAsBoolean(String key) {
|
||||
return Boolean.parseBoolean(configMap.get(key));
|
||||
String value = configMap.get(key);
|
||||
return value != null ? Boolean.parseBoolean(value) : false;
|
||||
}
|
||||
|
||||
public double getStateAsDouble(String key) {
|
||||
return Double.parseDouble(configMap.get(key));
|
||||
String value = configMap.get(key);
|
||||
return value != null ? Double.parseDouble(value) : 0.0;
|
||||
}
|
||||
|
||||
public char getStateAsChar(String key) {
|
||||
return configMap.get(key).charAt(0);
|
||||
String value = configMap.get(key);
|
||||
return value != null && !value.isEmpty() ? value.charAt(0) : '\0';
|
||||
}
|
||||
|
||||
public byte getStateAsByte(String key) {
|
||||
return Byte.parseByte(configMap.get(key));
|
||||
String value = configMap.get(key);
|
||||
return value != null ? Byte.parseByte(value) : 0;
|
||||
}
|
||||
|
||||
public short getStateAsShort(String key) {
|
||||
return Short.parseShort(configMap.get(key));
|
||||
String value = configMap.get(key);
|
||||
return value != null ? Short.parseShort(value) : 0;
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,41 @@ public class SystemInfoUtil {
|
||||
return System.getProperty("os.name");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前系统是否处于深色模式
|
||||
* @return true 表示处于深色模式,false 表示处于浅色模式
|
||||
*/
|
||||
public static boolean isSystemDarkMode() {
|
||||
try {
|
||||
if (System.getProperty("os.name").toLowerCase().contains("mac")) {
|
||||
Process process = Runtime.getRuntime().exec("defaults read -g AppleInterfaceStyle");
|
||||
return process.waitFor() == 0; // 命令成功返回表示深色模式
|
||||
}
|
||||
else if (System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||
String cmd = "reg query HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize "
|
||||
+ "/v AppsUseLightTheme";
|
||||
Process process = Runtime.getRuntime().exec(cmd);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.contains("REG_DWORD") && line.contains("0x0")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (System.getenv("XDG_CURRENT_DESKTOP") != null
|
||||
&& System.getenv("XDG_CURRENT_DESKTOP").toLowerCase().contains("gnome")) {
|
||||
Process process = Runtime.getRuntime().exec("gsettings get org.gnome.desktop.interface color-scheme");
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line = reader.readLine();
|
||||
return line != null && line.contains("dark");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to detect system dark mode", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 CPU 信息
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.axis.innovators.box.util;
|
||||
|
||||
import com.axis.innovators.box.gui.JarApiProfilingWindow;
|
||||
import com.axis.innovators.box.window.JarApiProfilingWindow;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
@@ -39,6 +39,9 @@ public class UserLocalInformation {
|
||||
OnlineVerification onlineVerification = OnlineVerification.validateLogin(
|
||||
verification,
|
||||
password);
|
||||
if (onlineVerification == null){
|
||||
return null;
|
||||
}
|
||||
return VerificationService.determineUserType(onlineVerification);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,32 +2,31 @@ package com.axis.innovators.box.verification;
|
||||
|
||||
/**
|
||||
* 在线验证用户身份
|
||||
* 用于报错用户的验证信息
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class OnlineVerification {
|
||||
public String onlineVerification;
|
||||
public UserTags userTags;
|
||||
public String password;
|
||||
|
||||
OnlineVerification(String onlineVerification, UserTags userTags){
|
||||
this.onlineVerification = onlineVerification;
|
||||
this.userTags = userTags;
|
||||
}
|
||||
/* 我是错误信息,要返回错误请修改我 */
|
||||
public static String errorMessage = "用户不存在";
|
||||
|
||||
/**
|
||||
* 验证登录
|
||||
* @param identifier
|
||||
* @param password
|
||||
* @param identifier 账号
|
||||
* @param password 密码
|
||||
*/
|
||||
OnlineVerification(String identifier, String password){
|
||||
this.onlineVerification = identifier;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public static OnlineVerification validateLogin(String identifier){
|
||||
return new OnlineVerification(identifier, UserTags.RegularUsers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证登录
|
||||
* @param identifier 账号
|
||||
* @param password 密码
|
||||
* @return 验证结果,如果返回null则表示验证失败,使用errorMessage获取验证失败的原因
|
||||
*/
|
||||
public static OnlineVerification validateLogin(String identifier, String password){
|
||||
return new OnlineVerification(identifier, password);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@ public enum UserTags {
|
||||
EnterpriseUsers;
|
||||
|
||||
private OnlineVerification onlineVerification;
|
||||
|
||||
/**
|
||||
* 设置用户组信息
|
||||
* @param onlineVerification 用户验证结果信息
|
||||
*/
|
||||
void setUser(OnlineVerification onlineVerification) {
|
||||
this.onlineVerification = onlineVerification;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,12 @@ package com.axis.innovators.box.verification;
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class VerificationService {
|
||||
|
||||
/**
|
||||
* 确定用户类型
|
||||
* @param identifier 用户
|
||||
* @return 用户类型
|
||||
*/
|
||||
public static UserTags determineUserType(OnlineVerification identifier) {
|
||||
UserTags userTags = UserTags.RegularUsers;
|
||||
userTags.setUser(identifier);
|
||||
@@ -19,6 +25,13 @@ public class VerificationService {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册用户
|
||||
* @param text
|
||||
* @param text1
|
||||
* @param pwd
|
||||
* @return
|
||||
*/
|
||||
public static boolean registerUser(String text, String text1, String pwd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import com.axis.innovators.box.AxisInnovatorsBox;
|
||||
import org.tzd.debug.ClassDebug;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import org.fife.ui.autocomplete.BasicCompletion;
|
||||
import org.fife.ui.autocomplete.DefaultCompletionProvider;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import com.axis.innovators.box.decompilation.gui.JavaPseudocodeGenerator;
|
||||
import com.axis.innovators.box.util.AdvancedJFileChooser;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
539
src/main/java/com/axis/innovators/box/window/LoginWindow.java
Normal file
@@ -0,0 +1,539 @@
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import com.axis.innovators.box.verification.OnlineVerification;
|
||||
import com.axis.innovators.box.verification.UserTags;
|
||||
import com.axis.innovators.box.verification.VerificationService;
|
||||
import com.formdev.flatlaf.FlatDarculaLaf;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.LineBorder;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 重构后的现代化单窗口登录/注册/找回密码界面(带平滑切换动画)
|
||||
* 保留原有验证逻辑接口调用(OnlineVerification / VerificationService)
|
||||
*
|
||||
* 说明:
|
||||
* - 单窗口(JDialog)内使用滑动动画切换视图(仿微软登录体验)。
|
||||
* - 所有子界面(登录/注册/找回密码)都在同一容器中切换,不再弹新窗口。
|
||||
* - 按钮与输入框固定宽度,避免被挤压变形。
|
||||
*
|
||||
* 注意:需要 flatlaf 依赖以呈现更现代的外观。
|
||||
*/
|
||||
public class LoginWindow {
|
||||
private static final AtomicReference<UserTags> loginResult = new AtomicReference<>(UserTags.None);
|
||||
|
||||
private final JDialog dialog;
|
||||
private final JLayeredPane layeredPane;
|
||||
private final int DIALOG_WIDTH = 460;
|
||||
private final int DIALOG_HEIGHT = 560;
|
||||
|
||||
// 登录面板中的控件需要在类域以便访问
|
||||
private JTextField loginEmailField;
|
||||
private JPasswordField loginPasswordField;
|
||||
|
||||
public static UserTags createAndShow() throws InterruptedException, InvocationTargetException {
|
||||
AtomicReference<UserTags> result = new AtomicReference<>(UserTags.None);
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
LoginWindow window = new LoginWindow();
|
||||
window.dialog.setVisible(true);
|
||||
result.set(loginResult.get());
|
||||
if (result.get() == UserTags.None) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
|
||||
public LoginWindow() {
|
||||
setupLookAndFeel();
|
||||
dialog = new JDialog((Frame) null, "AXIS 安全认证", true);
|
||||
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
dialog.setSize(DIALOG_WIDTH, DIALOG_HEIGHT);
|
||||
dialog.setResizable(false);
|
||||
dialog.setLocationRelativeTo(null);
|
||||
|
||||
// 根容器:深色背景并居中卡片
|
||||
JPanel root = new JPanel(new GridBagLayout());
|
||||
root.setBackground(new Color(0x202225));
|
||||
root.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
dialog.setContentPane(root);
|
||||
|
||||
// 卡片容器(居中)
|
||||
JPanel cardWrapper = new JPanel(null) {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension(DIALOG_WIDTH - 40, DIALOG_HEIGHT - 40);
|
||||
}
|
||||
};
|
||||
cardWrapper.setOpaque(false);
|
||||
cardWrapper.setPreferredSize(new Dimension(DIALOG_WIDTH - 40, DIALOG_HEIGHT - 40));
|
||||
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
root.add(cardWrapper, gbc);
|
||||
|
||||
// 分层面板用于动画
|
||||
layeredPane = new JLayeredPane();
|
||||
layeredPane.setBounds(0, 0, DIALOG_WIDTH - 40, DIALOG_HEIGHT - 40);
|
||||
cardWrapper.add(layeredPane);
|
||||
|
||||
// 卡片背景(圆角)
|
||||
JPanel backgroundCard = new JPanel();
|
||||
backgroundCard.setBackground(new Color(0x2A2E31));
|
||||
backgroundCard.setBorder(new RoundBorder(16, new Color(0x3A3F42)));
|
||||
backgroundCard.setBounds(0, 0, layeredPane.getWidth(), layeredPane.getHeight());
|
||||
backgroundCard.setLayout(null);
|
||||
layeredPane.add(backgroundCard, Integer.valueOf(0));
|
||||
|
||||
// 创建三个面板(宽度等于容器宽度),初始将登录面板放置在0位置
|
||||
JPanel loginPanel = buildLoginPanel();
|
||||
JPanel registerPanel = buildRegisterPanel();
|
||||
JPanel forgotPanel = buildForgotPanel();
|
||||
|
||||
int w = layeredPane.getWidth();
|
||||
int h = layeredPane.getHeight();
|
||||
|
||||
loginPanel.setBounds(0, 0, w, h);
|
||||
registerPanel.setBounds(w, 0, w, h); // 右侧预置
|
||||
forgotPanel.setBounds(w * 2, 0, w, h);
|
||||
|
||||
layeredPane.add(loginPanel, Integer.valueOf(1));
|
||||
layeredPane.add(registerPanel, Integer.valueOf(1));
|
||||
layeredPane.add(forgotPanel, Integer.valueOf(1));
|
||||
|
||||
dialog.pack();
|
||||
// ensure layered sizes match after pack
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
layeredPane.setBounds(0, 0, cardWrapper.getWidth(), cardWrapper.getHeight());
|
||||
backgroundCard.setBounds(0, 0, layeredPane.getWidth(), layeredPane.getHeight());
|
||||
int nw = layeredPane.getWidth(), nh = layeredPane.getHeight();
|
||||
loginPanel.setBounds(0, 0, nw, nh);
|
||||
registerPanel.setBounds(nw, 0, nw, nh);
|
||||
forgotPanel.setBounds(nw * 2, 0, nw, nh);
|
||||
});
|
||||
}
|
||||
|
||||
// 切换动画:direction = 1 表示向左滑动进入下一页(当前 -> 右侧),-1 表示向右滑动进入上一页
|
||||
private void slideTo(int targetIndex) {
|
||||
Component[] comps = layeredPane.getComponents();
|
||||
// 每个面板宽度
|
||||
int w = layeredPane.getWidth();
|
||||
// 当前最左边的x位置(找到最左的那个主要面板)
|
||||
// 我们采用简单方式:目标面板应该位于 x = targetIndex * w (0,1,2)
|
||||
int targetX = -targetIndex * w; // 我们会将所有面板整体平移,使目标显示在 x=0
|
||||
|
||||
// 获取当前 offset (使用第一个面板的 x 来代表整体偏移)
|
||||
int startOffset = 0;
|
||||
// find current offset by checking bounds of first panel (assume index 0 is login)
|
||||
if (comps.length > 0) {
|
||||
startOffset = comps[0].getX();
|
||||
}
|
||||
|
||||
int start = startOffset;
|
||||
int end = targetX;
|
||||
|
||||
int duration = 300; // ms
|
||||
int fps = 60;
|
||||
int delay = 1000 / fps;
|
||||
int steps = Math.max(1, duration / delay);
|
||||
final int[] step = {0};
|
||||
|
||||
Timer timer = new Timer(delay, null);
|
||||
timer.addActionListener((ActionEvent e) -> {
|
||||
step[0]++;
|
||||
double t = (double) step[0] / steps;
|
||||
// ease in-out cubic
|
||||
double tt = t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
||||
int cur = (int) Math.round(start + (end - start) * tt);
|
||||
|
||||
// 平移所有在 layeredPane 中(除背景)组件
|
||||
for (Component c : layeredPane.getComponents()) {
|
||||
if (c instanceof JPanel && c.isVisible()) {
|
||||
// 计算原始索引根据名字
|
||||
String name = c.getName();
|
||||
// 我们之前把panel放在 x = idx * w ; 现在把它设置为 idx*w + cur
|
||||
int idx = 0;
|
||||
if ("login".equals(name)) idx = 0;
|
||||
else if ("register".equals(name)) idx = 1;
|
||||
else if ("forgot".equals(name)) idx = 2;
|
||||
c.setLocation(idx * w + cur, 0);
|
||||
}
|
||||
}
|
||||
|
||||
layeredPane.repaint();
|
||||
|
||||
if (step[0] >= steps) {
|
||||
timer.stop();
|
||||
}
|
||||
});
|
||||
timer.setInitialDelay(0);
|
||||
timer.start();
|
||||
}
|
||||
|
||||
private JPanel buildLoginPanel() {
|
||||
JPanel p = new JPanel(null);
|
||||
p.setOpaque(false);
|
||||
p.setName("login");
|
||||
int w = DIALOG_WIDTH - 40;
|
||||
int h = DIALOG_HEIGHT - 40;
|
||||
|
||||
// 标题区
|
||||
JLabel appTitle = new JLabel("AXIS");
|
||||
appTitle.setFont(new Font("微软雅黑", Font.BOLD, 28));
|
||||
appTitle.setForeground(Color.WHITE);
|
||||
appTitle.setBounds(28, 20, w - 56, 36);
|
||||
|
||||
JLabel subtitle = new JLabel("安全认证 — 请登录以继续");
|
||||
subtitle.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
||||
subtitle.setForeground(new Color(0xA7AEB5));
|
||||
subtitle.setBounds(28, 56, w - 56, 18);
|
||||
|
||||
p.add(appTitle);
|
||||
p.add(subtitle);
|
||||
|
||||
// 卡片内控件起始 y
|
||||
int startY = 96;
|
||||
int fieldW = Math.min(360, w - 56);
|
||||
int fieldX = (w - fieldW) / 2;
|
||||
|
||||
// Email
|
||||
JLabel emailLabel = new JLabel("账号");
|
||||
emailLabel.setForeground(new Color(0x99A0A7));
|
||||
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
|
||||
emailLabel.setBounds(fieldX, startY, fieldW, 18);
|
||||
|
||||
loginEmailField = new JTextField();
|
||||
styleInput(loginEmailField);
|
||||
loginEmailField.setBounds(fieldX, startY + 22, fieldW, 44);
|
||||
loginEmailField.putClientProperty("JTextField.placeholderText", "邮箱或手机号");
|
||||
|
||||
// Password
|
||||
JLabel pwdLabel = new JLabel("密码");
|
||||
pwdLabel.setForeground(new Color(0x99A0A7));
|
||||
pwdLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
|
||||
pwdLabel.setBounds(fieldX, startY + 22 + 44 + 12, fieldW, 18);
|
||||
|
||||
loginPasswordField = new JPasswordField();
|
||||
styleInput(loginPasswordField);
|
||||
loginPasswordField.setBounds(fieldX, startY + 22 + 44 + 12 + 20, fieldW - 48, 44);
|
||||
loginPasswordField.putClientProperty("JTextField.placeholderText", "请输入密码");
|
||||
|
||||
// eye toggle
|
||||
JToggleButton eyeBtn = new JToggleButton("显示");
|
||||
eyeBtn.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
||||
eyeBtn.setFocusable(false);
|
||||
eyeBtn.setBorderPainted(false);
|
||||
eyeBtn.setContentAreaFilled(true);
|
||||
eyeBtn.setBackground(new Color(0x39424A));
|
||||
eyeBtn.setForeground(Color.WHITE);
|
||||
eyeBtn.setBounds(fieldX + fieldW - 44, startY + 22 + 44 + 12 + 20, 44, 44);
|
||||
eyeBtn.addActionListener(e -> {
|
||||
if (eyeBtn.isSelected()) loginPasswordField.setEchoChar((char) 0);
|
||||
else loginPasswordField.setEchoChar('•');
|
||||
});
|
||||
|
||||
// 登录按钮(占满宽度)
|
||||
JButton loginBtn = new JButton("立即登录");
|
||||
stylePrimaryButton(loginBtn);
|
||||
loginBtn.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 22, fieldW, 48);
|
||||
loginBtn.addActionListener(e -> doLogin());
|
||||
|
||||
// 链接区域(注册 / 忘记密码) — 点击切换到对应面板
|
||||
JButton toRegister = createTextLink("注册账号");
|
||||
toRegister.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 22 + 60, 120, 20);
|
||||
toRegister.addActionListener(e -> slideTo(1));
|
||||
|
||||
JButton toForgot = createTextLink("忘记密码");
|
||||
toForgot.setBounds(fieldX + fieldW - 120, startY + 22 + 44 + 12 + 20 + 44 + 22 + 60, 120, 20);
|
||||
toForgot.addActionListener(e -> slideTo(2));
|
||||
|
||||
// footer
|
||||
JLabel footer = new JLabel("使用你的 AXIS 账户进行登录。");
|
||||
footer.setForeground(new Color(0x8F969C));
|
||||
footer.setFont(new Font("微软雅黑", Font.PLAIN, 11));
|
||||
footer.setBounds(fieldX, h - 44, fieldW, 18);
|
||||
|
||||
p.add(emailLabel);
|
||||
p.add(loginEmailField);
|
||||
p.add(pwdLabel);
|
||||
p.add(loginPasswordField);
|
||||
p.add(eyeBtn);
|
||||
p.add(loginBtn);
|
||||
p.add(toRegister);
|
||||
p.add(toForgot);
|
||||
p.add(footer);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private JPanel buildRegisterPanel() {
|
||||
JPanel p = new JPanel(null);
|
||||
p.setOpaque(false);
|
||||
p.setName("register");
|
||||
int w = DIALOG_WIDTH - 40;
|
||||
int h = DIALOG_HEIGHT - 40;
|
||||
|
||||
JLabel title = new JLabel("创建账号");
|
||||
title.setFont(new Font("微软雅黑", Font.BOLD, 24));
|
||||
title.setForeground(Color.WHITE);
|
||||
title.setBounds(28, 20, w - 56, 36);
|
||||
|
||||
JLabel desc = new JLabel("快速创建你的 AXIS 帐号");
|
||||
desc.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
||||
desc.setForeground(new Color(0xA7AEB5));
|
||||
desc.setBounds(28, 56, w - 56, 18);
|
||||
|
||||
p.add(title);
|
||||
p.add(desc);
|
||||
|
||||
int startY = 96;
|
||||
int fieldW = Math.min(360, w - 56);
|
||||
int fieldX = (w - fieldW) / 2;
|
||||
|
||||
// 用户名
|
||||
JLabel nameLabel = new JLabel("用户名");
|
||||
nameLabel.setForeground(new Color(0x99A0A7));
|
||||
nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
|
||||
nameLabel.setBounds(fieldX, startY, fieldW, 18);
|
||||
|
||||
JTextField nameField = new JTextField();
|
||||
styleInput(nameField);
|
||||
nameField.setBounds(fieldX, startY + 22, fieldW, 44);
|
||||
|
||||
// 邮箱
|
||||
JLabel emailLabel = new JLabel("邮箱");
|
||||
emailLabel.setForeground(new Color(0x99A0A7));
|
||||
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
|
||||
emailLabel.setBounds(fieldX, startY + 22 + 44 + 12, fieldW, 18);
|
||||
|
||||
JTextField emailField = new JTextField();
|
||||
styleInput(emailField);
|
||||
emailField.setBounds(fieldX, startY + 22 + 44 + 12 + 20, fieldW, 44);
|
||||
|
||||
// 密码
|
||||
JLabel pwdLabel = new JLabel("密码");
|
||||
pwdLabel.setForeground(new Color(0x99A0A7));
|
||||
pwdLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
|
||||
pwdLabel.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12, fieldW, 18);
|
||||
|
||||
JPasswordField pwdField = new JPasswordField();
|
||||
styleInput(pwdField);
|
||||
pwdField.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20, fieldW, 44);
|
||||
|
||||
// 确认密码
|
||||
JLabel confirmLabel = new JLabel("确认密码");
|
||||
confirmLabel.setForeground(new Color(0x99A0A7));
|
||||
confirmLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
|
||||
confirmLabel.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 12, fieldW, 18);
|
||||
|
||||
JPasswordField confirmField = new JPasswordField();
|
||||
styleInput(confirmField);
|
||||
confirmField.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 12 + 20, fieldW, 44);
|
||||
|
||||
// 注册按钮
|
||||
JButton regBtn = new JButton("创建账号");
|
||||
stylePrimaryButton(regBtn);
|
||||
regBtn.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 18, fieldW, 48);
|
||||
|
||||
regBtn.addActionListener(e -> {
|
||||
String name = nameField.getText().trim();
|
||||
String email = emailField.getText().trim();
|
||||
String pwd = new String(pwdField.getPassword());
|
||||
String confirm = new String(confirmField.getPassword());
|
||||
|
||||
if (name.isEmpty() || email.isEmpty() || pwd.isEmpty() || confirm.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(dialog, "请完整填写注册信息", "注册错误", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (!pwd.equals(confirm)) {
|
||||
JOptionPane.showMessageDialog(dialog, "两次输入的密码不一致", "注册错误", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
boolean success = VerificationService.registerUser(name, email, pwd);
|
||||
if (success) {
|
||||
JOptionPane.showMessageDialog(dialog, "注册成功,请登录", "注册成功", JOptionPane.INFORMATION_MESSAGE);
|
||||
slideTo(0); // 回到登录页面
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(dialog, "注册失败,请检查信息", "注册错误", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
});
|
||||
|
||||
// 返回登录链接
|
||||
JButton back = createTextLink("返回登录");
|
||||
back.setBounds(fieldX, regBtn.getY() + regBtn.getHeight() + 12, 120, 20);
|
||||
back.addActionListener(e -> slideTo(0));
|
||||
|
||||
p.add(nameLabel);
|
||||
p.add(nameField);
|
||||
p.add(emailLabel);
|
||||
p.add(emailField);
|
||||
p.add(pwdLabel);
|
||||
p.add(pwdField);
|
||||
p.add(confirmLabel);
|
||||
p.add(confirmField);
|
||||
p.add(regBtn);
|
||||
p.add(back);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private JPanel buildForgotPanel() {
|
||||
JPanel p = new JPanel(null);
|
||||
p.setOpaque(false);
|
||||
p.setName("forgot");
|
||||
int w = DIALOG_WIDTH - 40;
|
||||
int h = DIALOG_HEIGHT - 40;
|
||||
|
||||
JLabel title = new JLabel("找回密码");
|
||||
title.setFont(new Font("微软雅黑", Font.BOLD, 24));
|
||||
title.setForeground(Color.WHITE);
|
||||
title.setBounds(28, 20, w - 56, 36);
|
||||
|
||||
JLabel desc = new JLabel("通过注册邮箱重置密码");
|
||||
desc.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
||||
desc.setForeground(new Color(0xA7AEB5));
|
||||
desc.setBounds(28, 56, w - 56, 18);
|
||||
|
||||
p.add(title);
|
||||
p.add(desc);
|
||||
|
||||
int startY = 110;
|
||||
int fieldW = Math.min(360, w - 56);
|
||||
int fieldX = (w - fieldW) / 2;
|
||||
|
||||
JLabel emailLabel = new JLabel("注册邮箱");
|
||||
emailLabel.setForeground(new Color(0x99A0A7));
|
||||
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
|
||||
emailLabel.setBounds(fieldX, startY, fieldW, 18);
|
||||
|
||||
JTextField emailField = new JTextField();
|
||||
styleInput(emailField);
|
||||
emailField.setBounds(fieldX, startY + 22, fieldW, 44);
|
||||
|
||||
JButton sendBtn = new JButton("发送重置邮件");
|
||||
stylePrimaryButton(sendBtn);
|
||||
sendBtn.setBounds(fieldX, startY + 22 + 44 + 22, fieldW, 48);
|
||||
|
||||
sendBtn.addActionListener(e -> {
|
||||
String email = emailField.getText().trim();
|
||||
if (email.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(dialog, "请输入注册邮箱", "错误", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (VerificationService.sendPasswordReset(email)) {
|
||||
JOptionPane.showMessageDialog(dialog, "重置邮件已发送,请查收", "成功", JOptionPane.INFORMATION_MESSAGE);
|
||||
slideTo(0);
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(dialog, "发送失败或邮箱未注册", "失败", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
});
|
||||
|
||||
JButton back = createTextLink("返回登录");
|
||||
back.setBounds(fieldX, sendBtn.getY() + sendBtn.getHeight() + 12, 120, 20);
|
||||
back.addActionListener(e -> slideTo(0));
|
||||
|
||||
p.add(emailLabel);
|
||||
p.add(emailField);
|
||||
p.add(sendBtn);
|
||||
p.add(back);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private void doLogin() {
|
||||
String email = loginEmailField.getText().trim();
|
||||
String password = new String(loginPasswordField.getPassword()).trim();
|
||||
|
||||
if (email.isEmpty() || password.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(dialog, "请输入完整的登录信息", "验证错误", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
OnlineVerification onlineVerification = OnlineVerification.validateLogin(email, password);
|
||||
if (onlineVerification == null) {
|
||||
String err = OnlineVerification.errorMessage != null && !OnlineVerification.errorMessage.trim().isEmpty()
|
||||
? OnlineVerification.errorMessage
|
||||
: "验证失败,请重试";
|
||||
JOptionPane.showMessageDialog(dialog, err, "验证错误", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
loginResult.set(VerificationService.determineUserType(onlineVerification));
|
||||
dialog.dispose();
|
||||
}
|
||||
|
||||
|
||||
// 通用输入框样式
|
||||
private void styleInput(JComponent comp) {
|
||||
comp.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
||||
comp.setBackground(new Color(0x2F3336));
|
||||
comp.setForeground(new Color(0xE8ECEF));
|
||||
comp.setBorder(BorderFactory.createCompoundBorder(
|
||||
new RoundBorder(8, new Color(0x3A3F42)),
|
||||
BorderFactory.createEmptyBorder(10, 12, 10, 12)
|
||||
));
|
||||
if (comp instanceof JTextComponent) ((JTextComponent) comp).setCaretColor(new Color(0x9AA0A6));
|
||||
}
|
||||
|
||||
// 主要操作按钮样式(主色)
|
||||
private void stylePrimaryButton(AbstractButton b) {
|
||||
b.setFont(new Font("微软雅黑", Font.BOLD, 14));
|
||||
b.setForeground(Color.WHITE);
|
||||
b.setBackground(new Color(0x2B79D0));
|
||||
b.setBorderPainted(false);
|
||||
b.setFocusPainted(false);
|
||||
b.setOpaque(true);
|
||||
b.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
}
|
||||
|
||||
private JButton createTextLink(String text) {
|
||||
JButton btn = new JButton(text);
|
||||
btn.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
||||
btn.setForeground(new Color(0x79A6FF));
|
||||
btn.setBorderPainted(false);
|
||||
btn.setContentAreaFilled(false);
|
||||
btn.setFocusPainted(false);
|
||||
btn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
return btn;
|
||||
}
|
||||
|
||||
private void setupLookAndFeel() {
|
||||
try {
|
||||
UIManager.setLookAndFeel(new FlatDarculaLaf());
|
||||
UIManager.put("Component.arc", 12);
|
||||
UIManager.put("TextComponent.arc", 12);
|
||||
UIManager.put("Button.arc", 10);
|
||||
|
||||
UIManager.put("Panel.background", new Color(0x202225));
|
||||
UIManager.put("TextComponent.background", new Color(0x2F3336));
|
||||
UIManager.put("TextComponent.foreground", new Color(0xE8ECEF));
|
||||
} catch (UnsupportedLookAndFeelException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// 圆角边框
|
||||
private static class RoundBorder extends LineBorder {
|
||||
private final int radius;
|
||||
|
||||
public RoundBorder(int radius, Color color) {
|
||||
super(color, 1);
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2.setColor(lineColor);
|
||||
g2.drawRoundRect(x, y, width - 1, height - 1, radius, radius);
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
1375
src/main/java/com/axis/innovators/box/window/MainWindow.java
Normal file
@@ -1,4 +1,4 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import org.tzd.debug.ClassDebug;
|
||||
import org.tzd.debug.GetInstance;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import com.axis.innovators.box.AxisInnovatorsBox;
|
||||
|
||||
@@ -0,0 +1,462 @@
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.geom.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 启动窗口的任务系统(已改造为现代流畅动画视觉效果)
|
||||
* 注意:保留了原有对外方法和签名(updateMainProgress/updateSubProgress/setTotalTasks/close)
|
||||
* 作者: tzdwindows 7(UI 改造版)
|
||||
*/
|
||||
public class ProgressBarManager extends WindowsJDialog {
|
||||
private JFrame loadingFrame;
|
||||
private SmoothProgressBar mainProgressBar;
|
||||
private SmoothProgressBar subProgressBar;
|
||||
private JLabel statusLabel;
|
||||
private JLabel timeLabel;
|
||||
private long startTime;
|
||||
|
||||
private int totalTasks;
|
||||
private int completedTasks;
|
||||
private Map<String, Integer> subTasks = new HashMap<>();
|
||||
|
||||
// 动画计时器(60FPS)
|
||||
private Timer animationTimer;
|
||||
|
||||
// 视觉参数
|
||||
private Color accentColor = new Color(0x00C2FF); // 科技感青蓝
|
||||
private Font uiFont;
|
||||
|
||||
public ProgressBarManager(String title, int totalTasks) {
|
||||
this.totalTasks = Math.max(1, totalTasks);
|
||||
this.completedTasks = 0;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
|
||||
// 尝试设置现代中文友好字体(Windows 常见)
|
||||
try {
|
||||
uiFont = new Font("Microsoft YaHei UI", Font.PLAIN, 13);
|
||||
// 若系统无该字体则 fallback
|
||||
if (!uiFont.getFamily().toLowerCase().contains("microsoft") &&
|
||||
!uiFont.getFamily().toLowerCase().contains("yahei")) {
|
||||
uiFont = new Font("Segoe UI", Font.PLAIN, 13);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
uiFont = new Font(Font.SANS_SERIF, Font.PLAIN, 13);
|
||||
}
|
||||
|
||||
loadingFrame = new JFrame(title);
|
||||
loadingFrame.setUndecorated(true);
|
||||
loadingFrame.setBackground(new Color(0, 0, 0, 0)); // 允许圆角透明背景
|
||||
loadingFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||
loadingFrame.setSize(520, 300);
|
||||
loadingFrame.setLocationRelativeTo(null);
|
||||
loadingFrame.setIconImage(LoadIcon.loadIcon("logo.png", 64).getImage());
|
||||
|
||||
// 主容器(带动画背景和圆角卡片)
|
||||
AnimatedBackgroundPanel root = new AnimatedBackgroundPanel();
|
||||
root.setLayout(new GridBagLayout());
|
||||
root.setBorder(BorderFactory.createEmptyBorder(18, 18, 18, 18));
|
||||
|
||||
// 卡片面板
|
||||
JPanel card = new JPanel() {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// 卡片阴影(简单外发光)
|
||||
int arc = 18;
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
|
||||
// 背景渐变
|
||||
GradientPaint gp = new GradientPaint(0, 0, new Color(20, 22, 25, 230),
|
||||
0, h, new Color(14, 16, 19, 230));
|
||||
g2.setPaint(gp);
|
||||
|
||||
// 圆角矩形
|
||||
RoundRectangle2D rr = new RoundRectangle2D.Float(6, 6, w - 12, h - 12, arc, arc);
|
||||
g2.fill(rr);
|
||||
|
||||
// 细微边框
|
||||
g2.setStroke(new BasicStroke(1f));
|
||||
g2.setColor(new Color(255, 255, 255, 10));
|
||||
g2.draw(rr);
|
||||
|
||||
g2.dispose();
|
||||
super.paintComponent(g);
|
||||
}
|
||||
};
|
||||
card.setOpaque(false);
|
||||
card.setLayout(new BorderLayout(12, 12));
|
||||
card.setPreferredSize(new Dimension(480, 240));
|
||||
card.setBorder(BorderFactory.createEmptyBorder(14, 14, 14, 14));
|
||||
|
||||
// 顶部 logo + 标题
|
||||
JPanel top = new JPanel(new BorderLayout());
|
||||
top.setOpaque(false);
|
||||
JLabel logoLabel = new JLabel(LoadIcon.loadIcon("logo.png", 48));
|
||||
logoLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 12));
|
||||
JLabel titleLabel = new JLabel(title);
|
||||
titleLabel.setFont(uiFont.deriveFont(Font.BOLD, 18f));
|
||||
titleLabel.setForeground(Color.WHITE);
|
||||
|
||||
top.add(logoLabel, BorderLayout.WEST);
|
||||
top.add(titleLabel, BorderLayout.CENTER);
|
||||
|
||||
// 中间进度区
|
||||
JPanel center = new JPanel(new GridBagLayout());
|
||||
center.setOpaque(false);
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
c.weightx = 1;
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
mainProgressBar = new SmoothProgressBar(0);
|
||||
mainProgressBar.setPreferredSize(new Dimension(420, 26));
|
||||
mainProgressBar.setAccentColor(accentColor);
|
||||
|
||||
subProgressBar = new SmoothProgressBar(0);
|
||||
subProgressBar.setPreferredSize(new Dimension(420, 18));
|
||||
subProgressBar.setAccentColor(new Color(0x6EE7FF));
|
||||
subProgressBar.setShowStripe(true);
|
||||
|
||||
center.add(mainProgressBar, c);
|
||||
c.gridy++;
|
||||
c.insets = new Insets(8, 0, 0, 0);
|
||||
center.add(subProgressBar, c);
|
||||
|
||||
// 底部文本
|
||||
JPanel bottom = new JPanel(new BorderLayout());
|
||||
bottom.setOpaque(false);
|
||||
statusLabel = new JLabel("Initializing...", SwingConstants.LEFT);
|
||||
statusLabel.setFont(uiFont.deriveFont(Font.PLAIN, 12f));
|
||||
statusLabel.setForeground(new Color(220, 230, 240));
|
||||
|
||||
timeLabel = new JLabel("Elapsed: 0s", SwingConstants.RIGHT);
|
||||
timeLabel.setFont(uiFont.deriveFont(Font.PLAIN, 12f));
|
||||
timeLabel.setForeground(new Color(180, 200, 215));
|
||||
|
||||
bottom.add(statusLabel, BorderLayout.WEST);
|
||||
bottom.add(timeLabel, BorderLayout.EAST);
|
||||
bottom.setBorder(BorderFactory.createEmptyBorder(8, 2, 2, 2));
|
||||
|
||||
card.add(top, BorderLayout.NORTH);
|
||||
card.add(center, BorderLayout.CENTER);
|
||||
card.add(bottom, BorderLayout.SOUTH);
|
||||
|
||||
root.add(card);
|
||||
loadingFrame.setContentPane(root);
|
||||
|
||||
// 拖动窗口支持(在无边框下)
|
||||
WindowDragger.makeDraggable(loadingFrame, card);
|
||||
|
||||
// 启动动画定时器
|
||||
animationTimer = new Timer(1000 / 60, e -> {
|
||||
boolean repaintNeeded = false;
|
||||
if (mainProgressBar.animateStep()) repaintNeeded = true;
|
||||
if (subProgressBar.animateStep()) repaintNeeded = true;
|
||||
root.advanceAnimation();
|
||||
updateTimeLabel();
|
||||
if (repaintNeeded) {
|
||||
root.repaint();
|
||||
} else {
|
||||
// 仍需刷新背景动画
|
||||
root.repaint();
|
||||
}
|
||||
});
|
||||
animationTimer.start();
|
||||
|
||||
loadingFrame.setVisible(true);
|
||||
|
||||
// 防止用户误操作关闭(保持原行为)
|
||||
loadingFrame.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
// DO NOTHING
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新主任务进度(对外接口保持不变)
|
||||
* @param completedTasks 已完成的主任务数量
|
||||
*/
|
||||
public void updateMainProgress(int completedTasks) {
|
||||
this.completedTasks = completedTasks;
|
||||
double progress = (completedTasks / (double) Math.max(1, totalTasks)) * 100.0;
|
||||
if (progress < 0) progress = 0;
|
||||
if (progress > 100) progress = 100;
|
||||
mainProgressBar.setTarget((int) Math.round(progress));
|
||||
statusLabel.setText("主任务: " + completedTasks + " / " + totalTasks + " (" + (int) progress + "%)");
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新子任务进度(对外接口保持不变)
|
||||
* @param subTaskName 子任务名称
|
||||
* @param subTaskCompleted 已完成的子任务数量
|
||||
* @param subTaskTotal 子任务总数
|
||||
*/
|
||||
public void updateSubProgress(String subTaskName, int subTaskCompleted, int subTaskTotal) {
|
||||
if (subTaskTotal <= 0) subTaskTotal = 1;
|
||||
subTasks.put(subTaskName, subTaskCompleted);
|
||||
double progress = (subTaskCompleted / (double) subTaskTotal) * 100.0;
|
||||
if (progress < 0) progress = 0;
|
||||
if (progress > 100) progress = 100;
|
||||
subProgressBar.setTarget((int) Math.round(progress));
|
||||
subProgressBar.setLabel(subTaskName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新总任务数
|
||||
*/
|
||||
public void setTotalTasks(int totalTasks) {
|
||||
this.totalTasks = Math.max(1, totalTasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭加载窗口
|
||||
*/
|
||||
public void close() {
|
||||
if (animationTimer != null && animationTimer.isRunning()) {
|
||||
animationTimer.stop();
|
||||
}
|
||||
loadingFrame.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新时间标签
|
||||
*/
|
||||
private void updateTimeLabel() {
|
||||
long elapsedTime = (System.currentTimeMillis() - startTime) / 1000;
|
||||
long hours = elapsedTime / 3600;
|
||||
long mins = (elapsedTime % 3600) / 60;
|
||||
long secs = elapsedTime % 60;
|
||||
if (hours > 0) {
|
||||
timeLabel.setText(String.format("Elapsed: %dh %02dm %02ds", hours, mins, secs));
|
||||
} else if (mins > 0) {
|
||||
timeLabel.setText(String.format("Elapsed: %dm %02ds", mins, secs));
|
||||
} else {
|
||||
timeLabel.setText(String.format("Elapsed: %ds", secs));
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
// 内部类:平滑进度条(支持插值动画、条纹、标签)
|
||||
// --------------------------
|
||||
private static class SmoothProgressBar extends JComponent {
|
||||
private int target = 0;
|
||||
private double displayed = 0.0;
|
||||
private int height = 20;
|
||||
private Color base = new Color(255, 255, 255, 18);
|
||||
private Color fill = new Color(0x00C2FF);
|
||||
private String label = "";
|
||||
private boolean showStripe = false;
|
||||
private Color stripeColor = new Color(255, 255, 255, 30);
|
||||
private double stripeOffset = 0.0;
|
||||
|
||||
public SmoothProgressBar(int initial) {
|
||||
this.target = Math.max(0, Math.min(100, initial));
|
||||
this.displayed = this.target;
|
||||
setOpaque(false);
|
||||
setPreferredSize(new Dimension(200, height));
|
||||
}
|
||||
|
||||
public void setAccentColor(Color c) {
|
||||
this.fill = c;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public void setShowStripe(boolean v) {
|
||||
this.showStripe = v;
|
||||
}
|
||||
|
||||
public void setTarget(int t) {
|
||||
t = Math.max(0, Math.min(100, t));
|
||||
this.target = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* 每帧推进插值,返回是否需要重绘
|
||||
*/
|
||||
public boolean animateStep() {
|
||||
// 平滑插值(阻尼)
|
||||
double diff = target - displayed;
|
||||
if (Math.abs(diff) < 0.02) {
|
||||
displayed = target;
|
||||
} else {
|
||||
displayed += diff * 0.18; // 阻尼因子(调整流畅度)
|
||||
}
|
||||
|
||||
// 条纹动画
|
||||
if (showStripe) {
|
||||
stripeOffset += 1.8;
|
||||
if (stripeOffset > 60) stripeOffset = 0;
|
||||
}
|
||||
|
||||
// 是否需要重绘
|
||||
return Math.abs(diff) > 0.001 || showStripe;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// 背景轨道
|
||||
RoundRectangle2D bg = new RoundRectangle2D.Float(0, 0, w, h, h, h);
|
||||
g2.setColor(base);
|
||||
g2.fill(bg);
|
||||
|
||||
// 阴影(内阴影模拟)
|
||||
g2.setColor(new Color(0, 0, 0, 30));
|
||||
g2.setStroke(new BasicStroke(1f));
|
||||
g2.draw(bg);
|
||||
|
||||
// 填充(渐变)
|
||||
int fillW = (int) Math.round((displayed / 100.0) * w);
|
||||
if (fillW > 0) {
|
||||
GradientPaint gp = new GradientPaint(0, 0, fill.brighter(), w, 0, fill.darker());
|
||||
RoundRectangle2D fg = new RoundRectangle2D.Float(0, 0, fillW, h, h, h);
|
||||
g2.setPaint(gp);
|
||||
g2.fill(fg);
|
||||
|
||||
// 发光边缘
|
||||
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.25f));
|
||||
g2.setColor(fill);
|
||||
g2.fill(new RoundRectangle2D.Float(0, -h / 3f, fillW, h + h / 3f, h, h));
|
||||
g2.setComposite(AlphaComposite.SrcOver);
|
||||
}
|
||||
|
||||
// 条纹效果
|
||||
if (showStripe && fillW > 6) {
|
||||
Shape clip = g2.getClip();
|
||||
g2.setClip(new RoundRectangle2D.Float(0, 0, fillW, h, h, h));
|
||||
int stripeW = 18;
|
||||
for (int x = -stripeW * 2; x < w + stripeW * 2; x += stripeW) {
|
||||
int sx = (int) (x + stripeOffset);
|
||||
Polygon p = new Polygon();
|
||||
p.addPoint(sx, 0);
|
||||
p.addPoint(sx + stripeW, 0);
|
||||
p.addPoint(sx + stripeW - 8, h);
|
||||
p.addPoint(sx - 8, h);
|
||||
g2.setColor(stripeColor);
|
||||
g2.fill(p);
|
||||
}
|
||||
g2.setClip(clip);
|
||||
}
|
||||
|
||||
// 文本显示(居中)
|
||||
String text;
|
||||
if (label != null && !label.isEmpty()) {
|
||||
text = label + " " + Math.round(displayed) + "%";
|
||||
} else {
|
||||
text = Math.round(displayed) + "%";
|
||||
}
|
||||
g2.setFont(new Font(Font.SANS_SERIF, Font.BOLD, Math.max(11, h - 6)));
|
||||
FontMetrics fm = g2.getFontMetrics();
|
||||
int tx = (w - fm.stringWidth(text)) / 2;
|
||||
int ty = (h + fm.getAscent() - fm.getDescent()) / 2;
|
||||
g2.setColor(new Color(255, 255, 255, 210));
|
||||
g2.drawString(text, tx, ty);
|
||||
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
// 内部类:带动画效果的背景面板(流动扫描线 + 颗粒/渐变)
|
||||
// --------------------------
|
||||
private class AnimatedBackgroundPanel extends JPanel {
|
||||
private double offset = 0;
|
||||
private double particlePhase = 0;
|
||||
|
||||
public AnimatedBackgroundPanel() {
|
||||
setOpaque(false);
|
||||
}
|
||||
|
||||
public void advanceAnimation() {
|
||||
offset += 0.9;
|
||||
if (offset > 2000) offset = 0;
|
||||
particlePhase += 0.02;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// 背景渐变(深色)
|
||||
Paint p = new GradientPaint(0, 0, new Color(8, 10, 12), w, h, new Color(18, 20, 24));
|
||||
g2.setPaint(p);
|
||||
g2.fillRect(0, 0, w, h);
|
||||
|
||||
// 斜向扫描线(细微)
|
||||
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.06f));
|
||||
g2.setColor(Color.WHITE);
|
||||
for (int i = -200; i < w + h; i += 40) {
|
||||
int x1 = i + (int) offset;
|
||||
int y1 = 0;
|
||||
int x2 = i - h + (int) offset;
|
||||
int y2 = h;
|
||||
g2.setStroke(new BasicStroke(2f));
|
||||
g2.drawLine(x1, y1, x2, y2);
|
||||
}
|
||||
g2.setComposite(AlphaComposite.SrcOver);
|
||||
|
||||
// 轻微颗粒(科技光斑)
|
||||
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.08f));
|
||||
for (int i = 0; i < 10; i++) {
|
||||
float px = (float) ((Math.sin(particlePhase + i) + 1) / 2.0 * w);
|
||||
float py = (float) ((Math.cos(particlePhase * 0.7 + i * 0.3) + 1) / 2.0 * h);
|
||||
int size = 6 + (i % 3) * 4;
|
||||
g2.fillOval((int) px, (int) py, size, size);
|
||||
}
|
||||
g2.setComposite(AlphaComposite.SrcOver);
|
||||
|
||||
g2.dispose();
|
||||
super.paintComponent(g);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
// 工具:使无边框窗口可拖动
|
||||
// --------------------------
|
||||
private static class WindowDragger {
|
||||
public static void makeDraggable(Window wnd, Component dragRegion) {
|
||||
final Point[] mouseDown = {null};
|
||||
dragRegion.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
mouseDown[0] = e.getPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
mouseDown[0] = null;
|
||||
}
|
||||
});
|
||||
dragRegion.addMouseMotionListener(new MouseMotionAdapter() {
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
if (mouseDown[0] != null) {
|
||||
Point curr = e.getLocationOnScreen();
|
||||
wnd.setLocation(curr.x - mouseDown[0].x, curr.y - mouseDown[0].y);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class ThemeColors {
|
||||
// 工具方法:使颜色变暗 (factor: 0~1,0.2表示变暗20%)
|
||||
public static Color darken(Color color, float factor) {
|
||||
return new Color(
|
||||
Math.max((int)(color.getRed() * (1 - factor)), 0),
|
||||
Math.max((int)(color.getGreen() * (1 - factor)), 0),
|
||||
Math.max((int)(color.getBlue() * (1 - factor)), 0),
|
||||
color.getAlpha()
|
||||
);
|
||||
}
|
||||
|
||||
// 工具方法:使颜色变亮 (factor: 0~1,0.2表示变亮20%)
|
||||
public static Color brighten(Color color, float factor) {
|
||||
return new Color(
|
||||
Math.min((int)(color.getRed() * (1 + factor)), 255),
|
||||
Math.min((int)(color.getGreen() * (1 + factor)), 255),
|
||||
Math.min((int)(color.getBlue() * (1 + factor)), 255),
|
||||
color.getAlpha()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package com.axis.innovators.box.gui;
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import com.axis.innovators.box.AxisInnovatorsBox;
|
||||
import com.axis.innovators.box.register.LanguageManager;
|
||||
import com.axis.innovators.box.register.RegistrationTopic;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -9,6 +10,7 @@ import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
|
||||
/**
|
||||
* 窗口
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class WindowsJDialog extends JDialog {
|
||||
@@ -48,4 +50,21 @@ public class WindowsJDialog extends JDialog {
|
||||
AxisInnovatorsBox.getMain().clearWindow(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测当前窗口的主题是否处于暗黑模式
|
||||
* @return true 处于暗黑模式,false 处于浅色模式
|
||||
*/
|
||||
public boolean isTopicDarkMode(){
|
||||
AxisInnovatorsBox axisInnovatorsBox = AxisInnovatorsBox.getMain();
|
||||
RegistrationTopic registrationTopic = axisInnovatorsBox.getRegistrationTopic();
|
||||
return registrationTopic.isDarkMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前窗口中更新主题
|
||||
*/
|
||||
public void updateTheme() {
|
||||
SwingUtilities.updateComponentTreeUI(this);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.axis.innovators.box.gui.renderer;
|
||||
package com.axis.innovators.box.window.renderer;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.AbstractBorder;
|
||||
4
src/main/java/org/tzd/explorer/DesktopIconRenderer.java
Normal file
@@ -0,0 +1,4 @@
|
||||
package org.tzd.explorer;
|
||||
|
||||
public class DesktopIconRenderer {
|
||||
}
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 146 KiB |
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 |
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -30,7 +30,7 @@
|
||||
</RollingFile>
|
||||
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="[%t] [%d{yyyy-MM-dd HH:mm:ss}] [%-5level] %msg%n%throwable"/>
|
||||
<PatternLayout pattern="[%t] [%d{yyyy-MM-dd HH:mm:ss}] [%level] [%logger-%method()] %msg%n%throwable"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#Updated configuration
|
||||
#Fri Mar 07 17:43:24 CST 2025
|
||||
password=safasf
|
||||
verification=aasfsaf
|
||||