Files
daluandou/.codely.packages/cn.tuanjie.codely.bridge@1.0.85-785dea6a/Editor/Tauri/WebView2Host.cs
T
huadexuan c09e15dd0e ```
feat(Materials): 更新CoreBattleGround材质贴图和缩放参数

更换了CoreBattleGround材质的主纹理,将纹理GUID从73e4d6cf089824c0cadfcefa5b15d306
更新为c6af6f473739fb546ac7775e5c957da1,并调整纹理缩放参数从{x: 3, y: 4}修改为
{x: 1, y: 1},同时移除了多余的_MetallicGlossMap配置项。
```
2026-09-28 17:58:15 +08:00

195 lines
7.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#if UNITY_EDITOR_WIN
using System;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
using static Cn.Tuanjie.Codely.Editor.NativeBrowserAPI;
using UnityTcp.Editor.Helpers;
namespace Cn.Tuanjie.Codely.Editor
{
/// <summary>
/// WebView2 嵌入式浏览器宿主(Windows)。
///
/// 所有线程安全、STA 消息泵、WebView2 COM 生命周期均由 NativeBrowser.dll 内部处理,
/// 本类只负责 P/Invoke 调用和委托 GC 保护。
/// </summary>
internal sealed class WebView2Host : IDisposable
{
IntPtr m_handle;
// 委托实例必须持有强引用,否则 GC 回收后 native 调用函数指针会崩溃
NavigationCallback m_onNav;
LoadCompletedCallback m_onLoad;
TitleChangedCallback m_onTitle;
MessageCallback m_onMessage;
DestroyedCallback m_onDestroyed;
public event Action OnParentDestroyed;
public event Action<string> OnTitleChanged;
public event Action<string> OnWebMessageReceived;
public event Action<bool> OnLoadCompleted;
public event Action<string> OnNavigationStarting;
public bool IsReady => m_handle != IntPtr.Zero;
/// <summary>
/// 原生 BrowserInstance 指针,用于序列化跨 Domain Reload 保活。
/// </summary>
public IntPtr Handle => m_handle;
// ── Domain Reload 恢复 ───────────────────────────────────────────────
/// <summary>
/// 从序列化的原生句柄恢复,复用已存活的 BrowserInstance。
/// 替换旧的(已失效的)托管回调指针为新委托,使 WebView2 继续正常工作。
/// </summary>
/// <returns>恢复成功返回 true;handle 无效或已被销毁返回 false。</returns>
public bool RestoreFromHandle(IntPtr handle)
{
if (handle == IntPtr.Zero) return false;
if (!NB_IsHandleValid(handle)) return false;
m_handle = handle;
// 重新构造委托实例(旧域的委托已失效,必须在新域中重新创建)
m_onNav = (url, _) => OnNavigationStarting?.Invoke(url);
m_onLoad = (ok, _) => OnLoadCompleted?.Invoke(ok);
m_onTitle = (t, _) => OnTitleChanged?.Invoke(t);
m_onMessage = (json, _) => OnWebMessageReceived?.Invoke(json);
m_onDestroyed = (_) => OnParentDestroyed?.Invoke();
var cb = new Callbacks
{
onNavigationStarting = m_onNav,
onLoadCompleted = m_onLoad,
onTitleChanged = m_onTitle,
onWebMessageReceived = m_onMessage,
onParentDestroyed = m_onDestroyed,
ctx = IntPtr.Zero,
};
NB_SetCallbacks(handle, ref cb);
return true;
}
// ── 初始化(同步返回,WebView2 在 native STA 线程异步初始化)──────────
public void Init(IntPtr parentHwnd, string initialUrl = "about:blank")
{
if (!NB_IsRuntimeAvailable())
throw new InvalidOperationException(
"[WebView2Host] WebView2 Runtime not installed. " +
"Please install Microsoft Edge or WebView2 Evergreen Runtime.");
string version = Marshal.PtrToStringUni(NB_GetRuntimeVersion());
CodelyLogger.Log($"[WebView2Host] Runtime version: {version}");
string dataFolder = Path.Combine(
Application.temporaryCachePath,
"WebView2_" + parentHwnd.ToInt64().ToString("X"))
.Replace('/', '\\');
// 构造回调结构,成员委托持有到 Dispose
m_onNav = (url, _) => OnNavigationStarting?.Invoke(url);
m_onLoad = (ok, _) => OnLoadCompleted?.Invoke(ok);
m_onTitle = (t, _) => OnTitleChanged?.Invoke(t);
m_onMessage = (json, _) => OnWebMessageReceived?.Invoke(json);
m_onDestroyed = (_) => OnParentDestroyed?.Invoke();
var cb = new Callbacks
{
onNavigationStarting = m_onNav,
onLoadCompleted = m_onLoad,
onTitleChanged = m_onTitle,
onWebMessageReceived = m_onMessage,
onParentDestroyed = m_onDestroyed,
ctx = IntPtr.Zero,
};
m_handle = NB_Create(parentHwnd, dataFolder, initialUrl, ref cb);
if (m_handle == IntPtr.Zero)
throw new Exception("[WebView2Host] NB_Create failed (check native logs)");
CodelyLogger.Log($"[WebView2Host] Created, handle=0x{m_handle:X}, url={initialUrl}");
}
// ── 公开 API(线程安全,native 内部转发到 STA 线程)──────────────────
public void Navigate(string url)
{
if (IsReady && !string.IsNullOrEmpty(url))
NB_Navigate(m_handle, url);
}
public void GoBack() { if (IsReady) NB_GoBack(m_handle); }
public void GoForward() { if (IsReady) NB_GoForward(m_handle); }
public void Reload() { if (IsReady) NB_Reload(m_handle); }
public void ExecuteScript(string js)
{
if (IsReady && !string.IsNullOrEmpty(js))
NB_ExecuteScript(m_handle, js);
}
public void PostWebMessage(string json)
{
if (IsReady && !string.IsNullOrEmpty(json))
NB_PostWebMessage(m_handle, json);
}
public void SetTopInset(int topPx)
{
if (IsReady) NB_SetTopInset(m_handle, topPx);
}
/// <summary>
/// Set WebView bounds in parent client coordinates (pixels).
/// Use this to match the exact content area from Unity layout (embedRect).
/// </summary>
public void Resize(int x, int y, int w, int h)
{
if (IsReady && w > 0 && h > 0) NB_Resize(m_handle, x, y, w, h);
}
public void SetVisible(bool visible)
{
if (IsReady) NB_SetVisible(m_handle, visible);
}
public void ShowDevTools()
{
if (IsReady) NB_OpenDevTools(m_handle);
}
/// <summary>
/// 将键盘焦点移入 WebView2 内容。嵌入 Unity GUIView 后,导航完成时父窗口仍持有
/// Win32 焦点,HTML 的 autofocus / element.focus() 不会显示光标也收不到键盘输入;
/// 调用本方法把焦点交给内容(等价于浏览器中窗口被激活时内容自动获得焦点)。
/// </summary>
public void MoveFocus()
{
if (IsReady) NB_MoveFocus(m_handle);
}
// ── 清理 ──────────────────────────────────────────────────────────────
public void Dispose()
{
if (m_handle != IntPtr.Zero)
{
NB_Destroy(m_handle);
m_handle = IntPtr.Zero;
}
// 清空委托引用,让 GC 可以回收(各类型不同,不能链式赋值)
m_onNav = null;
m_onLoad = null;
m_onTitle = null;
m_onMessage = null;
m_onDestroyed = null;
}
}
}
#endif