Files
msime-windows/server/src/webview2/windows_webview2.h
T
時崎 ギャラクシー bcbbdcf74c Merge pull request #903 from Tokisaki-Galaxy/feat/skin-schema-v2
feat(server): 外部皮肤新增 schema_version 2,WebView2 与 D2D 双后端支持九宫格贴图与自由挂件
2026-10-13 00:57:14 +08:00

220 lines
10 KiB
C++

#pragma once
#include "WebView2.h"
#include "fmt/core.h"
#include "fmt/xchar.h"
#include <boost/locale.hpp>
#include <fstream>
#include <functional>
#include <sstream>
#include <string>
#include <vector>
#include <wrl.h>
#include <wrl/client.h>
#include <wil/com.h>
#include <dcomp.h>
#include <cmath>
#include "utils/common_utils.h"
#include "utils/window_utils.h"
#include "global/globals.h"
using namespace Microsoft::WRL;
const std::wstring LocalAssetsPath = fmt::format( //
L"{}\\assets", //
CommonUtils::get_ime_data_path_w() //
);
inline std::wstring GetLocalAssetsPath()
{
// Prefer a live lookup: static LocalAssetsPath can be empty if CRT init ran
// before the user profile environment was fully available.
const std::wstring live = fmt::format(L"{}\\assets", CommonUtils::get_ime_data_path_w());
if (live.empty() || live[0] == L'\\')
{
return LocalAssetsPath;
}
return live;
}
void UpdateHtmlContentWithJavaScript( //
ComPtr<ICoreWebView2> webview, //
const std::wstring &newContent //
); //
struct CandidateWindowContent;
void UpdateHtmlContentWithJavaScript(ComPtr<ICoreWebView2> webview, const std::wstring &newContent,
std::function<void()> onComplete, const CandidateWindowContent *content = nullptr);
//
// 候选窗口 webview
//
inline ComPtr<ICoreWebView2Controller> webviewControllerCandWnd;
inline ComPtr<ICoreWebView2> webviewCandWnd;
inline ComPtr<ICoreWebView2_3> webview3CandWnd;
inline ComPtr<ICoreWebView2Controller2> webviewController2CandWnd;
inline ComPtr<ICoreWebView2Controller3> webviewController3CandWnd;
inline EventRegistrationToken candidateRasterizationScaleChangedToken{};
inline bool candidateRasterizationScaleChangedRegistered = false;
inline std::wstring HTMLStringCandWnd = LR"()";
inline std::wstring BodyStringCandWnd = LR"()";
inline std::wstring MeasureStringCandWnd = LR"()";
inline std::wstring StrCandWnd = L"";
int PrepareHtmlForWnds();
bool ApplyConfiguredCandidateWindowLayout();
bool ApplyConfiguredUiThemes();
// Reconcile the actual WebView skin even when another thread consumed the
// config file timestamp before the UI timer observed it.
bool ApplyConfiguredCandidateSkinIfChanged();
bool ForceReloadConfiguredCandidateSkin();
uint64_t GetCandidateSkinReloadRevision();
bool ApplyConfiguredCandidateAppearance();
bool ApplyConfiguredFloatingToolbarAppearance();
bool ApplyConfiguredFloatingToolbarAppearance(std::function<void()> onComplete);
// Geometry declared by the active external skin package. Built-in skins return 0.
double GetActiveCandidateSkinDecorationTopDip();
double GetActiveCandidateSkinDecorationWidthDip();
// v2 皮肤为挂在卡片外沿的挂件声明的预留空白(DIP)。没有就返回 0。
double GetActiveCandidateSkinOuterTopDip();
double GetActiveCandidateSkinOuterRightDip();
double GetActiveCandidateSkinOuterBottomDip();
double GetActiveCandidateSkinOuterLeftDip();
// Push half-monitor CSS max width/height (DIP) into a small-window page.
void InjectSurfaceViewportLimits(ICoreWebView2 *webview, HWND hwnd);
// WebView2 rasterization scale includes both monitor DPI and the user's text
// scaling. It can therefore differ from GetDpiForWindow()/96.
FLOAT GetWebViewRasterizationScale(HWND hwnd);
HalfScreenDipLimits QueryWebViewHalfScreenDipLimitsForHwnd(HWND hwnd);
HalfScreenDipLimits QueryCandidateHalfScreenDipLimitsForPoint(HWND hwnd, POINT pt);
void ResetContainerHoverCandWnd(ComPtr<ICoreWebView2> webview);
void DisableMouseForAWhileWhenShownCandWnd(ComPtr<ICoreWebView2> webview);
// CSS hover is gated on .hover-active. WebView2 synthesizes mousemove when
// the HWND is placed under a stationary cursor; arm only after the OS cursor
// has actually moved (GetCursorPos), not after a Chromium mouse event.
void DisarmCandidatePointerHover();
void MaybeArmCandidatePointerHover();
// Owns what it renders: the measure and FineTune callbacks outlive the locals that build it.
struct CandidateWindowContent
{
std::wstring preedit;
Global::CandidatePageSnapshotPtr page;
bool show_preedit;
};
void InflateCandWnd(const CandidateWindowContent &content, std::function<void()> onComplete = nullptr);
// DIP size of #realContainer from the last slot-update script, or {0,0}.
std::pair<double, double> LastCandidateSlotMeasuredSize();
void InflateMeasureDivCandWnd(const CandidateWindowContent &content, std::function<void()> onComplete = nullptr);
void InitSmallWindowWebviews(HWND candHwnd, HWND menuHwnd, HWND ftbHwnd);
// appearance.ui_backend = "webview2" but no usable WebView2 Runtime: run this process on the D2D
// renderer so typing still works. Call after InitImeConfig and before CreateCandidateWindow.
void FallBackToD2dIfWebView2RuntimeMissing();
void ShutdownWebviews();
void UpdateSmallWindowWebviewVisibility(HWND hwnd, bool visible);
// Kick (or retry) shared small-window WebView2 init. Returns true when the menu
// controller already exists and the tray menu may be shown.
bool PrepareTrayMenuWebviewForShow();
// Remember a candidate show that arrived before this host's WebView2 was
// ready, and replay WM_SHOW_MAIN_WINDOW once navigation completes.
void DeferCandidateShowUntilWebviewReady();
void MaybeFlushPendingCandidateShow();
// After the candidate controller exists, pin TOPMOST and renotify WebView2
// (same uiAccess rule as the tray menu: never pin before CreateCoreWebView2Controller).
void RaiseCandidateHostForShow(const wchar_t *reason);
// Expand WebView bounds beyond the host client so horizontal measure/layout
// is not constrained by a still-narrow HWND.
void PrepareCandidateWebViewBoundsForMeasure(HWND hwnd);
void SyncCandidateWebViewBoundsToHost(HWND hwnd);
// Capture geometry-only browser/CSS state for clipping investigations. Never
// includes DOM text or candidate content.
void LogCandidateLayoutSnapshot(const wchar_t *stage);
// uiAccess + HWND WebView2: request TOPMOST on first real show. If WebViews are
// not ready yet, the request is queued and applied after all small-window
// navigations complete (avoid TopMost during CreateCoreWebView2Controller).
// Returns true when TOPMOST is already in effect after the call.
bool EnsureSmallWindowsTopmost(const wchar_t *reason);
// True only once all three hosts have reached the topmost band. The band is
// entered one host at a time on a timer, so this stays false for a few seconds
// after the WebViews report ready.
bool AreSmallWindowsTopmostApplied();
bool AreSmallWindowWebviewsReady();
bool IsCandidateWebviewReady();
// True once the floating toolbar's WebView2 has completed its first navigation
// and can actually paint. Its host must stay on-monitor and "visible" until
// then, otherwise WebView2 never finishes raster setup for it.
bool IsFloatingToolbarWebviewReady();
void NotifyFloatingToolbarPageReady();
// True while the post-navigation paint grace is active: the host is kept shown
// briefly after NavigationCompleted, then visibility is reconciled for real.
bool IsFloatingToolbarPaintGraceActive();
void BeginFloatingToolbarPaintGrace();
void EndFloatingToolbarPaintGrace();
void ClearFloatingToolbarNavigationState();
// True only when the tray menu is actually open in front of the user: its
// WebView2 has painted at least once and the host is neither hidden nor still
// DWM-cloaked for warmup. IsWindowVisible() alone cannot answer this, because
// the warmup leaves the host "visible" for the whole of startup.
bool IsTrayMenuOpenToUser();
// Controller-side view of the toolbar for the diagnostic trace: what WebView2
// itself believes about visibility and the area it was told to paint. Returns
// false when no controller exists yet. Must be called on the UI thread.
bool GetFloatingToolbarWebviewState(bool &isVisible, RECT &bounds);
// Same for the tray menu, whose blank-but-clickable failure mode needs exactly
// this to be separated from a host that was never uncloaked.
bool GetTrayMenuWebviewState(bool &isVisible, RECT &bounds);
bool GetCandidateWebviewState(bool &isVisible, RECT &bounds);
void LogSmallWindowReadyGate(const wchar_t *context);
// Lift the tray menu to the front of the small-window topmost band (e.g. after
// FTB was pinned last), renotifying its controller about the band change. Only
// call this once the menu has content: during WebView2 warmup a topmost host is
// what breaks controller creation and compositing. The show path may call it
// while still cloaked, since by then the first navigation has completed.
void RaiseTrayMenuAboveSmallWindows(const wchar_t *reason);
//
// 菜单窗口 webview
//
inline ComPtr<ICoreWebView2Controller> webviewControllerMenuWnd;
inline ComPtr<ICoreWebView2> webviewMenuWnd;
inline ComPtr<ICoreWebView2_3> webview3MenuWnd;
inline ComPtr<ICoreWebView2Controller2> webviewController2MenuWnd;
inline std::wstring HTMLStringMenuWnd = LR"()";
// Sync tray-menu floating-toolbar toggle and input-scheme check with config.toml.
void SyncMenuFloatingToolbarToggle();
// Collapse the tray-menu input-scheme submenu before the menu is shown again.
void ResetMenuInputSchemeSubmenu();
//
// floating toolbar 窗口 webview
//
inline ComPtr<ICoreWebView2Controller> webviewControllerFtbWnd;
inline ComPtr<ICoreWebView2> webviewFtbWnd;
inline ComPtr<ICoreWebView2_3> webview3FtbWnd;
inline ComPtr<ICoreWebView2Controller2> webviewController2FtbWnd;
inline std::wstring HTMLStringFtbWnd = LR"()";
// These entry points always update the canonical toolbar state, even when the
// WebView is not ready. A successful FTB navigation replays the complete state.
void UpdateFtbCnEnState(ComPtr<ICoreWebView2> webview, int cnEnState);
void UpdateFtbCnEnAndPuncState(ComPtr<ICoreWebView2> webview, int cnEnState, int puncState);
void UpdateFtbCnEnAndDoubleSingleAndPuncState( //
ComPtr<ICoreWebView2> webview, //
int cnEnState, //
int doubleSingleByteState, //
int puncState, //
int capsLockState = 0 //
);
void UpdateFtbPuncState(ComPtr<ICoreWebView2> webview, int puncState);
void UpdateFtbDoubleSingleByteState(ComPtr<ICoreWebView2> webview, int doubleSingleByteState);
void UpdateFtbEnglishInputModeState(ComPtr<ICoreWebView2> webview, int enabled);
void UpdateFtbCapsLockState(ComPtr<ICoreWebView2> webview, int enabled);
void UpdateFtbCharacterSetState(ComPtr<ICoreWebView2> webview);
void UpdateFtbInputModeState(ComPtr<ICoreWebView2> webview, int japaneseMode);
// Reapply configured optional buttons to the live toolbar and resize its host.
void ApplyConfiguredFloatingToolbarItems();