WebView
查看 Markdown通过系统 WebView2 在游戏窗口内渲染网页。
include/XBase/WebView.h · XBase::WebView
网页由系统 WebView2 渲染在游戏窗口内的独立子窗口里;XBase 负责生命周期、面板位置、输入焦点与状态查询,宿主只提交 URL、可见性和尺寸。
<游戏根目录>\XBase\WebView2Loader.dll,宿主发布包不需要自带IsRuntimeAvailable() 判断,不可用时不要启用相关 UI。struct State {
bool initialized;
bool visible;
bool loading;
bool canGoBack;
bool canGoForward;
int lastError;
std::string url;
std::string title;
};
bool IsRuntimeAvailable();
bool Init();
bool IsInitialized();
void NotifyGameInit();
void Process();
void Shutdown();
bool Navigate(const std::string& url);
bool SetHtml(const std::string& html);
void Reload();
bool GoBack();
bool GoForward();
void SetZoom(float factor);
void SetVisible(bool visible);
bool IsVisible();
void SetBounds(const Rect& bounds);
State GetState();
bool Close();
bool UsesCaptureMode();
void DrawPanel(const Rect& bounds);
void ForwardPanelInput(const Rect& bounds, Vec2 mouse, bool mouseDown, float wheelDelta);
void SetStateCallback(StateCallback callback);
using MessageHandler = std::function<void(const std::string& json)>;
void SetMessageHandler(MessageHandler handler);
bool PostJson(const std::string& json);
bool InjectScript(const std::string& script);
bool MapFolder(const std::string& hostName, const std::string& folderPath);SetMessageHandler() 接收网页用 chrome.webview.postMessage 发来的原始 JSON,回调在游戏线程执行;PostJson() 反向投递,网页用 chrome.webview 的 message 事件接收;InjectScript() 在页面脚本之前注入代码,控制器尚未创建时会排队。需要现成的调用约定时用 WebBridge。
独占全屏时 DWM 不合成子窗口,HWND 覆盖层永远不可见,此时 UsesCaptureMode() 返回真,宿主改用抓帧贴图:
DrawPanel() 在 SetBounds() 提交的同一矩形内绘制最新一帧(抓帧解码后上传 D3D9 贴图),宿主在占位控件之后调用即可。ForwardPanelInput() 把鼠标位置换算成页面坐标,转发悬停、点击与滚轮(UI::IsLastItemHovered() + UI::GetMousePosition() + Hooks::ConsumeWheelDelta())。Hooks::SetWindowMode(WindowMode::Borderless));窗口由 DWM 合成后本页自动回落到原生覆盖层,抓帧模式随之关闭。SetVisible(false) 只隐藏面板,浏览器与宿主窗口保持存活,再次显示几乎瞬时完成,对应窗口的最小化Close() 释放浏览器与宿主窗口,内存归还系统,之后再次 SetVisible(true) 或 Navigate 会重新创建,对应窗口的关闭Shutdown() 是卸载路径,与两者都不同Init() 只检查运行时并登记请求,不会立刻创建浏览器;真正的环境与控制器创建延迟到宿主请求页面之后(Navigate、SetHtml、SetVisible(true) 都会登记请求)的 Process() 安全点执行,因此不会在渲染钩子里重入消息循环,未使用网页时也不会启动浏览器进程。GetState().initialized 为真,之前提交的 Navigate/SetHtml 会自动补发,若期间已请求显示则直接可见。SetBounds() 接收与 UI::GetDisplaySize() 同坐标系的矩形(ImGui 显示像素,通常按菜单内容区计算),XBase 内部按游戏客户区比例换算并裁剪到游戏窗口内。UI::GetCurrentWindowRect() 求交集)。SetVisible(false)。SetVisible(false);NotifyGameInit() 会自动隐藏。%APPDATA%\com.yuinijika.xbase\webview2,由 XBase 创建,不落在游戏目录内。SetStateCallback() 可在游戏线程收到变化通知。| Capability | SA | VC | III |
|---|---|---|---|
| WebView | ✅ | ✅ | ✅ |
实现位于 Win32/COM 层,不访问游戏内存与插件地址;真实可用性由 IsRuntimeAvailable() 在运行时判定。
file: 协议下每个页面都是独立安全源,模块脚本与带 crossorigin 的请求会被拦下,因此本地界面必须映射成虚拟主机再加载:
XBase::WebView::Init();
XBase::WebView::MapFolder("xmenu.local", XBase::Platform::CurrentModuleDirectory());
XBase::WebView::Navigate("https://xmenu.local/ui.html");映射目录下的脚本、样式与 fetch('./config.json') 都按同源处理。
独占全屏时 HWND 覆盖层不参与合成,UsesCaptureMode() 为真,此时:
DrawPanel(bounds) 把抓帧画面画出来,必须放在真实的 ImGui 窗口内,否则会被塞进默认 Debug 窗口ForwardPanelInput(bounds, mouse, mouseDown, wheelDelta) 转发鼠标与滚轮
评论