feat(browser): 添加自定义浏览器创建回调和链接打开方式设置
- 新增 BrowserCreationCallback 接口,用于自定义浏览器布局 - 在 Builder 中添加 setBrowserCreationCallback 方法设置回调 - 增加 openLinksInBrowser 方法设置链接打开方式 - 修改 onBeforePopup 和 onBeforeBrowse 方法以支持新设置 - 优化 CefAppManager 中的语言设置和暗黑模式配置 - 更新 MainApplication 中的浏览器窗口创建方式
This commit is contained in:
@@ -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;
|
||||
@@ -43,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);
|
||||
@@ -53,7 +55,7 @@ public class BrowserWindow extends JFrame {
|
||||
private boolean maximizable = true; // 默认允许最大化
|
||||
private boolean minimizable = true; // 默认允许最小化
|
||||
private String htmlUrl = "";
|
||||
|
||||
private boolean openLinksInExternalBrowser = true; // 默认使用外部浏览器
|
||||
|
||||
public Builder resizable(boolean resizable) {
|
||||
this.resizable = resizable;
|
||||
@@ -65,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;
|
||||
@@ -74,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 标题
|
||||
@@ -244,12 +293,20 @@ public class BrowserWindow extends JFrame {
|
||||
@Override
|
||||
public boolean onBeforePopup(CefBrowser browser, CefFrame frame,
|
||||
String targetUrl, String targetFrameName) {
|
||||
// 处理弹出窗口:根据配置决定打开方式
|
||||
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; // 拦截弹窗并在当前窗口打开
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -257,13 +314,20 @@ public class BrowserWindow extends JFrame {
|
||||
@Override
|
||||
public boolean onBeforeBrowse(CefBrowser browser, CefFrame frame,
|
||||
CefRequest request, boolean userGesture, boolean isRedirect) {
|
||||
// 处理主窗口导航
|
||||
if (userGesture) {
|
||||
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;
|
||||
}
|
||||
@@ -371,7 +435,34 @@ public class BrowserWindow extends JFrame {
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -54,7 +55,8 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
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;
|
||||
@@ -83,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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浏览器窗口大小
|
||||
@@ -157,6 +206,7 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private BrowserWindowJDialog(Builder builder) {
|
||||
// 根据父窗口是否存在,设置是否为模态对话框
|
||||
super(builder.parentFrame, builder.title, builder.parentFrame != null);
|
||||
@@ -191,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) {
|
||||
@@ -211,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(
|
||||
@@ -256,12 +298,20 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
@Override
|
||||
public boolean onBeforePopup(CefBrowser browser, CefFrame frame,
|
||||
String targetUrl, String targetFrameName) {
|
||||
// 处理弹出窗口:根据配置决定打开方式
|
||||
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; // 拦截弹窗并在当前窗口打开
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -269,13 +319,20 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
@Override
|
||||
public boolean onBeforeBrowse(CefBrowser browser, CefFrame frame,
|
||||
CefRequest request, boolean userGesture, boolean isRedirect) {
|
||||
// 处理主窗口导航
|
||||
if (userGesture) {
|
||||
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;
|
||||
}
|
||||
@@ -383,7 +440,36 @@ public class BrowserWindowJDialog extends JDialog {
|
||||
}
|
||||
|
||||
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,6 +1,7 @@
|
||||
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;
|
||||
@@ -72,21 +73,35 @@ public class CefAppManager {
|
||||
//settings.background_color = new Color(255, 255, 255, 0);
|
||||
settings.command_line_args_disabled = false;
|
||||
|
||||
boolean isDarkTheme = isDarkTheme();
|
||||
// 转换语言标识格式:system:zh_CN -> zh-CN
|
||||
|
||||
if (isDarkTheme) {
|
||||
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("Dark theme settings applied");
|
||||
|
||||
logger.info("CEF commandLine: {}", commandLine.getSwitches());
|
||||
}
|
||||
});
|
||||
|
||||
logger.info("Optimized CEF settings initialized");
|
||||
} finally {
|
||||
|
||||
@@ -41,7 +41,8 @@ public class MainApplication {
|
||||
WindowRegistry.getInstance().createNewWindow("main", builder ->
|
||||
window.set(builder.title("Axis Innovators Box AI 工具箱")
|
||||
.size(1280, 720)
|
||||
.htmlPath("C:\\Users\\Administrator\\Downloads\\deepseek_html_20250817_d94647.html")
|
||||
.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
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user