diff --git a/server/src/utils/webview_utils.cpp b/server/src/utils/webview_utils.cpp index 3cc02db4..8982645c 100644 --- a/server/src/utils/webview_utils.cpp +++ b/server/src/utils/webview_utils.cpp @@ -61,8 +61,17 @@ void GetContainerSizeCand(ComPtr webview, std::functionExecuteScript( // diff --git a/server/src/webview2/windows_webview2.cpp b/server/src/webview2/windows_webview2.cpp index 351aac5a..03943365 100644 --- a/server/src/webview2/windows_webview2.cpp +++ b/server/src/webview2/windows_webview2.cpp @@ -366,33 +366,81 @@ inline std::wstring GetAppdataPath() void UpdateHtmlContentWithJavaScript(ComPtr webview, const std::wstring &newContent) { - if (webview != nullptr) + UpdateHtmlContentWithJavaScript(webview, newContent, nullptr); +} + +void UpdateHtmlContentWithJavaScript(ComPtr webview, const std::wstring &newContent, + std::function onComplete) +{ + if (!webview) { - std::wstring script; - script.reserve(newContent.length() + 512); - - script.append(L"document.getElementById('realContainer').innerHTML = `"); - script.append(newContent); - script.append(L"`;\n"); - script.append(L"window.ClearState();\n"); - script.append(L"var el = document.getElementById('realContainerParent');\n"); - script.append(L"if (el) {\n"); - script.append(L" el.style.marginTop = \""); - script.append(std::to_wstring(Global::MarginTop)); - script.append(L"px\";\n"); - script.append(L"}\n"); - script.append(L"if (window.SetCandidateSelection) { window.SetCandidateSelection("); - script.append(std::to_wstring(Global::candidate_ui.selected_index_in_page)); - script.append(L"); }\n"); - script.append(L"if (window.SetCandidatePreeditVisible) { window.SetCandidatePreeditVisible("); - script.append(GetConfiguredCandidateWindowPreeditStyle() == "empty" ? L"false" : L"true"); - script.append(L"); }\n"); - script.append(L"if (window.SetPreeditCaret) { window.SetPreeditCaret(); }\n"); - - // OutputDebugString(fmt::format(L"[msime]: UpdateHtmlContentWithJavaScript: {}", script).c_str()); - - webview->ExecuteScript(script.c_str(), nullptr); + if (onComplete) + { + onComplete(); + } + return; } + + std::wstring script; + script.reserve(newContent.length() + 512); + + script.append(L"document.getElementById('realContainer').innerHTML = `"); + script.append(newContent); + script.append(L"`;\n"); + script.append(L"window.ClearState();\n"); + script.append(L"var el = document.getElementById('realContainerParent');\n"); + script.append(L"if (el) {\n"); + script.append(L" el.style.marginTop = \""); + script.append(std::to_wstring(Global::MarginTop)); + script.append(L"px\";\n"); + script.append(L"}\n"); + script.append(L"if (window.SetCandidateSelection) { window.SetCandidateSelection("); + script.append(std::to_wstring(Global::candidate_ui.selected_index_in_page)); + script.append(L"); }\n"); + script.append(L"if (window.SetCandidatePreeditVisible) { window.SetCandidatePreeditVisible("); + script.append(GetConfiguredCandidateWindowPreeditStyle() == "empty" ? L"false" : L"true"); + script.append(L"); }\n"); + script.append(L"if (window.SetPreeditCaret) { window.SetPreeditCaret(); }\n"); + + if (!onComplete) + { + webview->ExecuteScript(script.c_str(), nullptr); + return; + } + + webview->ExecuteScript( + script.c_str(), + Callback( + [onComplete](HRESULT, LPCWSTR) -> HRESULT { + onComplete(); + return S_OK; + }) + .Get()); +} + +void PrepareCandidateWebViewBoundsForMeasure(HWND hwnd) +{ + if (!webviewControllerCandWnd || !hwnd) + { + return; + } + RECT bounds{}; + GetClientRect(hwnd, &bounds); + bounds.right += candidateBoundRightExtra; + bounds.bottom += candidateBoundBottomExtra; + webviewControllerCandWnd->put_Bounds(bounds); +} + +void SyncCandidateWebViewBoundsToHost(HWND hwnd) +{ + if (!webviewControllerCandWnd || !hwnd) + { + return; + } + RECT bounds{}; + GetClientRect(hwnd, &bounds); + webviewControllerCandWnd->put_Bounds(bounds); + webviewControllerCandWnd->NotifyParentWindowPositionChanged(); } int PrepareHtmlForWnds() @@ -555,6 +603,11 @@ window.mouseBlockTimeout = setTimeout(() => { } void InflateCandWnd(std::wstring &str) +{ + InflateCandWnd(str, nullptr); +} + +void InflateCandWnd(std::wstring &str, std::function onComplete) { std::wstringstream wss(str); std::wstring token; @@ -565,7 +618,7 @@ void InflateCandWnd(std::wstring &str) words.push_back(token); } - int size = words.size(); + int size = static_cast(words.size()); while (words.size() < 9) { @@ -588,11 +641,10 @@ void InflateCandWnd(std::wstring &str) if (size < 9) { size_t pos = result.find(fmt::format(L"", size)); - // result = result.substr(0, pos) + L""; result = result.substr(0, pos); } - UpdateHtmlContentWithJavaScript(webviewCandWnd, result); + UpdateHtmlContentWithJavaScript(webviewCandWnd, result, std::move(onComplete)); } void InflateMeasureDivCandWnd(std::wstring &str) diff --git a/server/src/webview2/windows_webview2.h b/server/src/webview2/windows_webview2.h index d6125e62..595cdae4 100644 --- a/server/src/webview2/windows_webview2.h +++ b/server/src/webview2/windows_webview2.h @@ -5,6 +5,7 @@ #include "fmt/xchar.h" #include #include +#include #include #include #include @@ -28,6 +29,11 @@ void UpdateHtmlContentWithJavaScript( // ComPtr webview, // const std::wstring &newContent // ); // +void UpdateHtmlContentWithJavaScript( // + ComPtr webview, // + const std::wstring &newContent, // + std::function onComplete // +); // // // 候选窗口 webview @@ -48,10 +54,15 @@ bool ApplyConfiguredUiThemes(); void ResetContainerHoverCandWnd(ComPtr webview); void DisableMouseForAWhileWhenShownCandWnd(ComPtr webview); void InflateCandWnd(std::wstring &str); +void InflateCandWnd(std::wstring &str, std::function onComplete); void InflateMeasureDivCandWnd(std::wstring &str); void InitSmallWindowWebviews(HWND candHwnd, HWND menuHwnd, HWND ftbHwnd); void ShutdownWebviews(); void UpdateSmallWindowWebviewVisibility(HWND hwnd, bool visible); +// 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); // 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 diff --git a/server/src/window/ime_windows.cpp b/server/src/window/ime_windows.cpp index 3a2af42b..86f187ae 100644 --- a/server/src/window/ime_windows.cpp +++ b/server/src/window/ime_windows.cpp @@ -1481,6 +1481,8 @@ int FineTuneWindow(HWND hwnd) LogSmallWindowReadyGate(L"fine-tune-no-webview"); return 0; } + // Give horizontal measure an unconstrained viewport before reading DOM size. + PrepareCandidateWebViewBoundsForMeasure(hwnd); std::shared_ptr> properPos = std::make_shared>(); GetContainerSizeCand(webviewCandWnd, [flag, // scale, // @@ -1512,7 +1514,6 @@ int FineTuneWindow(HWND hwnd) OutputDebugStringW(L"[msime-webview] candidate fine-tune abandoned: composition and candidates empty\n"); return; } - InflateCandWnd(str); int newWidth = 0; int newHeight = 0; @@ -1532,6 +1533,10 @@ int FineTuneWindow(HWND hwnd) { return; } + + // Resize host + WebView first, then paint visible content. Inflating + // into a still-narrow viewport lets horizontal candidates wrap and + // flash a taller intermediate frame before the final single-line layout. const BOOL positioned = SetWindowPos( // hwnd, // nullptr, // @@ -1541,20 +1546,27 @@ int FineTuneWindow(HWND hwnd) newHeight, // newFlag // ); - SetHostWindowCloaked(hwnd, false); - UpdateSmallWindowWebviewVisibility(hwnd, true); - SyncHostWebViewBounds(::webviewControllerCandWnd.Get(), hwnd); - RECT actualRect{}; - GetWindowRect(hwnd, &actualRect); - OutputDebugStringW(fmt::format( - L"[msime-webview] candidate window positioned: requested_pos=({},{}), requested_size=({},{}), " - L"flags=0x{:X}, success={}, GetLastError={}, actual_rect=[{},{},{},{}], visible={}, " - L"topmost_applied={}, measure=({:.1f},{:.1f})\n", - properPos->first, properPos->second, newWidth, newHeight, newFlag, positioned != FALSE, - positioned ? 0 : GetLastError(), actualRect.left, actualRect.top, actualRect.right, actualRect.bottom, - IsWindowVisible(hwnd) != FALSE, AreSmallWindowsTopmostApplied(), containerSize.first, - containerSize.second) - .c_str()); + SyncCandidateWebViewBoundsToHost(hwnd); + + InflateCandWnd(str, [hwnd, positioned, newWidth, newHeight, newFlag, properPos, containerSize]() { + if (!::is_global_wnd_cand_shown) + { + return; + } + SetHostWindowCloaked(hwnd, false); + UpdateSmallWindowWebviewVisibility(hwnd, true); + RECT actualRect{}; + GetWindowRect(hwnd, &actualRect); + OutputDebugStringW(fmt::format( + L"[msime-webview] candidate window positioned: requested_pos=({},{}), requested_size=({},{}), " + L"flags=0x{:X}, success={}, GetLastError={}, actual_rect=[{},{},{},{}], visible={}, " + L"topmost_applied={}, measure=({:.1f},{:.1f})\n", + properPos->first, properPos->second, newWidth, newHeight, newFlag, positioned != FALSE, + positioned ? 0 : GetLastError(), actualRect.left, actualRect.top, actualRect.right, actualRect.bottom, + IsWindowVisible(hwnd) != FALSE, AreSmallWindowsTopmostApplied(), containerSize.first, + containerSize.second) + .c_str()); + }); }); return 0; }