Revert the WebView2 changes and temporarily make it switchable between rendering backends.
This commit is contained in:
1 parent
cf547ac6ed
commit
b0156540c0
8 files changed
+451
-64
No files matched your search
@@ -86,6 +86,9 @@ linear_step = 1
|
||||
[appearance]
|
||||
# 主题模式,可选:"dark", "light", "system"
|
||||
theme_mode = "system"
|
||||
# 候选窗 / 悬浮工具栏 / 托盘菜单渲染,可选:"d2d", "webview2"
|
||||
# 更改后需重启输入法进程生效
|
||||
ui_backend = "d2d"
|
||||
# 分表面主题,可选:"follow", "dark", "light"(follow = 跟随 theme_mode)
|
||||
theme_settings = "follow"
|
||||
theme_cand = "follow"
|
||||
|
||||
@@ -103,6 +103,8 @@ bool g_paired_punctuation_enabled = true;
|
||||
std::string g_punctuation_lock = "follow";
|
||||
std::string g_candidate_window_layout = "vertical";
|
||||
bool g_candidate_window_follow_cursor = true;
|
||||
std::string g_ui_backend = "d2d";
|
||||
std::string g_ui_backend_active = "d2d";
|
||||
std::string g_candidate_skin = "fluent";
|
||||
std::string g_candidate_window_preedit_style = "pinyin";
|
||||
std::string g_theme_mode = "system";
|
||||
@@ -124,6 +126,13 @@ FrequencyAdjustmentConfig g_frequency_adjustment;
|
||||
std::filesystem::path g_config_path;
|
||||
std::optional<std::filesystem::file_time_type> g_config_last_write_time;
|
||||
|
||||
std::string NormalizeSmallWindowUiBackend(const std::string &value)
|
||||
{
|
||||
if (value == "webview2" || value == "webview" || value == "web")
|
||||
return "webview2";
|
||||
return "d2d";
|
||||
}
|
||||
|
||||
bool IsValidCandidateSkinId(const std::string &skin)
|
||||
{
|
||||
if (skin.empty() || skin.size() > 64 || !std::isalnum(static_cast<unsigned char>(skin.front())))
|
||||
@@ -786,6 +795,8 @@ bool LoadImeConfig()
|
||||
const std::string layout = tbl["appearance"]["candidate_window_layout"].value_or(std::string("vertical"));
|
||||
g_candidate_window_layout = layout == "horizontal" ? "horizontal" : "vertical";
|
||||
g_candidate_window_follow_cursor = tbl["appearance"]["candidate_window_follow_cursor"].value_or(true);
|
||||
g_ui_backend = NormalizeSmallWindowUiBackend(
|
||||
tbl["appearance"]["ui_backend"].value_or(std::string("d2d")));
|
||||
const std::string skin = tbl["appearance"]["candidate_skin"].value_or(std::string("fluent"));
|
||||
g_candidate_skin = IsValidCandidateSkinId(skin) ? skin : "fluent";
|
||||
{
|
||||
@@ -1239,6 +1250,7 @@ void InitImeConfig()
|
||||
(void)0;
|
||||
#endif
|
||||
}
|
||||
g_ui_backend_active = g_ui_backend;
|
||||
}
|
||||
|
||||
bool ReloadImeConfigIfChanged()
|
||||
@@ -2347,6 +2359,27 @@ bool SetConfiguredCandidateWindowFollowCursor(bool enabled)
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string &GetConfiguredUiBackend()
|
||||
{
|
||||
return g_ui_backend;
|
||||
}
|
||||
|
||||
bool SetConfiguredUiBackend(const std::string &backend)
|
||||
{
|
||||
const std::string normalized = NormalizeSmallWindowUiBackend(backend);
|
||||
if (!WriteConfiguredValue("appearance", "ui_backend", EscapeTomlBasicString(normalized)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
g_ui_backend = normalized;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UseD2dSmallWindowUi()
|
||||
{
|
||||
return g_ui_backend_active != "webview2";
|
||||
}
|
||||
|
||||
const std::string &GetConfiguredCandidateSkin()
|
||||
{
|
||||
return g_candidate_skin;
|
||||
|
||||
@@ -280,6 +280,13 @@ const std::string &GetConfiguredCandidateWindowLayout();
|
||||
bool SetConfiguredCandidateWindowLayout(const std::string &layout);
|
||||
bool GetConfiguredCandidateWindowFollowCursor();
|
||||
bool SetConfiguredCandidateWindowFollowCursor(bool enabled);
|
||||
// "d2d" | "webview2" — candidate, floating toolbar, and tray menu renderer.
|
||||
// Changing this writes config immediately; the process snapshot does not switch
|
||||
// until the IME server is restarted.
|
||||
const std::string &GetConfiguredUiBackend();
|
||||
bool SetConfiguredUiBackend(const std::string &backend);
|
||||
// Process-lifetime renderer chosen at InitImeConfig. Independent of later reloads.
|
||||
bool UseD2dSmallWindowUi();
|
||||
// "fluent" | "wechat" | "graphite" | "willow_green" — candidate-window and floating-toolbar skin.
|
||||
const std::string &GetConfiguredCandidateSkin();
|
||||
bool SetConfiguredCandidateSkin(const std::string &skin);
|
||||
|
||||
@@ -350,7 +350,9 @@ void PostConfig(bool refresh_skin_catalog = false)
|
||||
{"r_mode", GetConfiguredRModeEnabled()},
|
||||
{"clipboard_history", GetConfiguredClipboardHistoryEnabled()}}},
|
||||
{"appearance",
|
||||
{{"candidate_window_layout", GetConfiguredCandidateWindowLayout()},
|
||||
{{"ui_backend", GetConfiguredUiBackend()},
|
||||
{"candidate_window_layout", GetConfiguredCandidateWindowLayout()},
|
||||
{"candidate_window_follow_cursor", GetConfiguredCandidateWindowFollowCursor()},
|
||||
{"candidate_skin", GetConfiguredCandidateSkin()},
|
||||
{"candidate_window_preedit_style", GetConfiguredCandidateWindowPreeditStyle()},
|
||||
{"tsf_preedit_style", GetConfiguredTsfPreeditStyle()},
|
||||
@@ -477,8 +479,12 @@ bool ApplyConfigUpdate(const json::object &data)
|
||||
return SetConfiguredPunctuationLock(json::value_to<std::string>(data.at("value")));
|
||||
if (path == "appearance.tsf_preedit_style")
|
||||
return SetConfiguredTsfPreeditStyle(json::value_to<std::string>(data.at("value")));
|
||||
if (path == "appearance.ui_backend")
|
||||
return SetConfiguredUiBackend(json::value_to<std::string>(data.at("value")));
|
||||
if (path == "appearance.candidate_window_layout")
|
||||
return SetConfiguredCandidateWindowLayout(json::value_to<std::string>(data.at("value")));
|
||||
if (path == "appearance.candidate_window_follow_cursor")
|
||||
return SetConfiguredCandidateWindowFollowCursor(json::value_to<bool>(data.at("value")));
|
||||
if (path == "appearance.candidate_skin")
|
||||
return SetConfiguredCandidateSkin(json::value_to<std::string>(data.at("value")));
|
||||
if (path == "appearance.candidate_window_preedit_style")
|
||||
|
||||
@@ -141,6 +141,10 @@ void GetCandidateCardSize(
|
||||
}).Get());
|
||||
DIAG_LOGF(L"ui-measure submit box={} parent={} max_dip=({:.1f},{:.1f}) hr={:#x}", boxId, parentId,
|
||||
maxWidthDip, maxHeightDip, static_cast<unsigned>(submitHr));
|
||||
if (FAILED(submitHr))
|
||||
{
|
||||
callback({0.0, 0.0});
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ enum class SmallWindowInitState
|
||||
SmallWindowInitState smallWindowInitState = SmallWindowInitState::Idle;
|
||||
int smallWindowInitAttempts = 0;
|
||||
bool pendingTrayMenuShow = false;
|
||||
bool pendingCandidateShow = false;
|
||||
bool floatingToolbarNavigationReady = false;
|
||||
// After NavigationCompleted, keep the host shown briefly so a cold WebView2
|
||||
// user-data folder can finish first paint; then reconcile hide/show for real.
|
||||
@@ -533,12 +534,16 @@ void RequestNextSmallWindowController()
|
||||
HRESULT (*request)(HWND, HRESULT, ICoreWebView2Environment *);
|
||||
};
|
||||
const Host hosts[] = {
|
||||
{L"cand", smallWindowCandHwnd, webviewControllerCandWnd != nullptr, &OnEnvironmentCreated},
|
||||
{L"menu", smallWindowMenuHwnd, webviewControllerMenuWnd != nullptr, &OnMenuWindowEnvironmentCreated},
|
||||
{L"ftb", smallWindowFtbHwnd, webviewControllerFtbWnd != nullptr, &OnFtbWindowEnvironmentCreated},
|
||||
};
|
||||
constexpr int kHostCount = 3;
|
||||
|
||||
int chosen = -1;
|
||||
for (int i = 0; i < 1; ++i)
|
||||
for (int step = 1; step <= kHostCount; ++step)
|
||||
{
|
||||
const int i = (lastFailedSmallWindowHostIndex + step) % kHostCount;
|
||||
if (!hosts[i].hasController && hosts[i].hwnd)
|
||||
{
|
||||
chosen = i;
|
||||
@@ -548,6 +553,7 @@ void RequestNextSmallWindowController()
|
||||
if (chosen < 0)
|
||||
{
|
||||
MaybeFlushPendingTrayMenuShow();
|
||||
MaybeFlushPendingCandidateShow();
|
||||
return;
|
||||
}
|
||||
const Host &host = hosts[chosen];
|
||||
@@ -594,7 +600,7 @@ void OnSmallWindowControllerSettled(HRESULT hr)
|
||||
|
||||
void BeginSmallWindowWebviewEnvironmentCreate()
|
||||
{
|
||||
if (!smallWindowMenuHwnd || !smallWindowFtbHwnd)
|
||||
if (!smallWindowCandHwnd || !smallWindowMenuHwnd || !smallWindowFtbHwnd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -668,11 +674,14 @@ FloatingToolbarState floatingToolbarState;
|
||||
|
||||
bool AreSmallWindowWebviewsReadyUnlocked()
|
||||
{
|
||||
const bool candReady =
|
||||
CandidatePresenter::Instance().IsBound() ||
|
||||
(candidateNavigationReady && webviewCandWnd != nullptr && webviewControllerCandWnd != nullptr);
|
||||
const bool menuReady = TrayMenuPresenter::Instance().IsBound() ||
|
||||
(menuNavigationReady && webviewMenuWnd != nullptr && webviewControllerMenuWnd != nullptr);
|
||||
const bool ftbReady = FloatingToolbarPresenter::Instance().IsBound() ||
|
||||
(floatingToolbarNavigationReady && webviewFtbWnd != nullptr && webviewControllerFtbWnd != nullptr);
|
||||
return menuReady && ftbReady;
|
||||
return candReady && menuReady && ftbReady;
|
||||
}
|
||||
|
||||
void CancelStaggeredTopmost()
|
||||
@@ -765,9 +774,20 @@ void ApplySmallWindowTopmostStep(SmallWindowTopmostStep step)
|
||||
{
|
||||
case SmallWindowTopmostStep::Candidate:
|
||||
PinHostTopmost(::global_hwnd);
|
||||
if (::is_global_wnd_cand_shown && ::global_hwnd && CandidatePresenter::Instance().IsBound())
|
||||
if (CandidatePresenter::Instance().IsBound())
|
||||
{
|
||||
CandidatePresenter::Instance().Present();
|
||||
if (::is_global_wnd_cand_shown && ::global_hwnd)
|
||||
{
|
||||
CandidatePresenter::Instance().Present();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RenotifyControllerAfterPin(webviewControllerCandWnd.Get(), ::global_hwnd);
|
||||
if (::is_global_wnd_cand_shown && ::global_hwnd)
|
||||
{
|
||||
FineTuneWindow(::global_hwnd);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -897,6 +917,7 @@ void NotifySmallWindowNavigationReady(bool &readyFlag, const wchar_t *which)
|
||||
LogSmallWindowReadyGateUnlocked(L"after-nav-ready");
|
||||
TryApplyPendingLazyTopmost(L"pending-after-nav-ready");
|
||||
MaybeFlushPendingTrayMenuShow();
|
||||
MaybeFlushPendingCandidateShow();
|
||||
}
|
||||
|
||||
bool UpdateBinaryState(int value, int &state)
|
||||
@@ -1128,6 +1149,56 @@ void RaiseTrayMenuAboveSmallWindows(const wchar_t *reason)
|
||||
RenotifyControllerAfterPin(webviewControllerMenuWnd.Get(), ::global_hwnd_menu);
|
||||
}
|
||||
|
||||
void DeferCandidateShowUntilWebviewReady()
|
||||
{
|
||||
pendingCandidateShow = true;
|
||||
CAND_DIAG_LOGF(L"candidate show deferred until webview ready {}", DescribeCandidateHostState());
|
||||
}
|
||||
|
||||
void MaybeFlushPendingCandidateShow()
|
||||
{
|
||||
if (!pendingCandidateShow || !::global_hwnd || !IsWindow(::global_hwnd))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!::is_global_wnd_cand_shown)
|
||||
{
|
||||
pendingCandidateShow = false;
|
||||
return;
|
||||
}
|
||||
if (!IsCandidateWebviewReady())
|
||||
{
|
||||
return;
|
||||
}
|
||||
pendingCandidateShow = false;
|
||||
g_candidate_show_msg_pending.store(false);
|
||||
CAND_DIAG_LOGF(L"candidate replaying show that was deferred until the webview was ready");
|
||||
PostMessage(::global_hwnd, WM_SHOW_MAIN_WINDOW, 0, 0);
|
||||
}
|
||||
|
||||
void RaiseCandidateHostForShow(const wchar_t *reason)
|
||||
{
|
||||
if (!::global_hwnd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (CandidatePresenter::Instance().IsBound())
|
||||
{
|
||||
constexpr UINT flag = SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE;
|
||||
SetWindowPos(::global_hwnd, HWND_TOPMOST, 0, 0, 0, 0, flag);
|
||||
return;
|
||||
}
|
||||
if (!webviewControllerCandWnd)
|
||||
{
|
||||
CAND_DIAG_LOGF(L"candidate raise reason={} skipped: no controller yet", reason);
|
||||
return;
|
||||
}
|
||||
CAND_DIAG_LOGF(L"candidate raise reason={} nav_ready={} {}", reason, candidateNavigationReady,
|
||||
DescribeCandidateHostState());
|
||||
PinHostTopmost(::global_hwnd);
|
||||
RenotifyControllerAfterPin(webviewControllerCandWnd.Get(), ::global_hwnd);
|
||||
}
|
||||
|
||||
bool AreSmallWindowsTopmostApplied()
|
||||
{
|
||||
return smallWindowTopmostApplied;
|
||||
@@ -1140,7 +1211,8 @@ bool AreSmallWindowWebviewsReady()
|
||||
|
||||
bool IsCandidateWebviewReady()
|
||||
{
|
||||
return CandidatePresenter::Instance().IsBound();
|
||||
return CandidatePresenter::Instance().IsBound() ||
|
||||
(candidateNavigationReady && webviewCandWnd != nullptr && webviewControllerCandWnd != nullptr);
|
||||
}
|
||||
|
||||
bool IsFloatingToolbarWebviewReady()
|
||||
@@ -1583,6 +1655,58 @@ bool InjectExternalSkinCssFile(
|
||||
return true;
|
||||
}
|
||||
|
||||
void AppendExternalCandidateColorCss(std::wstring &css, const CandidateSkinCatalog::CandidateColors &colors)
|
||||
{
|
||||
auto add = [&](const std::string &value, const wchar_t *selector, const wchar_t *property) {
|
||||
if (value.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
css.append(selector);
|
||||
css.append(L" { ");
|
||||
css.append(property);
|
||||
css.append(L": ");
|
||||
css.append(string_to_wstring(value));
|
||||
css.append(L"; }\n");
|
||||
};
|
||||
add(colors.accent, L".cursor, .first::before", L"background");
|
||||
add(colors.selected, L".first, .cand.first", L"background-color");
|
||||
add(colors.hover, L".hover-active .cand:not(.first):hover", L"background-color");
|
||||
add(colors.surface, L".container", L"background");
|
||||
add(colors.border, L".container", L"border-color");
|
||||
add(colors.text, L".container", L"color");
|
||||
add(colors.number, L".num, .cand-no", L"color");
|
||||
if (colors.showSelectedBar.has_value() && !*colors.showSelectedBar)
|
||||
{
|
||||
css.append(L".first::before { display: none; }\n");
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring BuildExternalCandidateSkinCss(const CandidateSkinCatalog::Package &skin, const std::wstring &skinsRoot)
|
||||
{
|
||||
std::wstring css;
|
||||
if (skin.decorationTopDip > 0.0)
|
||||
{
|
||||
std::wstring preview = L"none";
|
||||
if (!skin.preview.empty())
|
||||
{
|
||||
preview = EmbedSkinCssUrl(skinsRoot, skin.id, string_to_wstring(skin.preview));
|
||||
}
|
||||
css.append(L".containerParent { padding-top: var(--msime-skin-decoration-top, 0px); "
|
||||
L"position: relative; box-sizing: border-box; }\n"
|
||||
L".containerParent:not(:empty)::before { content: \"\"; position: absolute; "
|
||||
L"z-index: 0; top: 0; right: 0; width: var(--msime-skin-decoration-width, 0px); "
|
||||
L"height: var(--msime-skin-decoration-top, 0px); background: ");
|
||||
css.append(preview);
|
||||
css.append(L" center / contain no-repeat; pointer-events: none; }\n"
|
||||
L".container { position: relative; z-index: 1; "
|
||||
L"min-width: max(7em, var(--msime-skin-min-width, 0px)); }\n");
|
||||
}
|
||||
const bool light = ResolveConfiguredTheme(GetConfiguredThemeCand()) == "light";
|
||||
AppendExternalCandidateColorCss(css, light ? skin.light : skin.dark);
|
||||
return css;
|
||||
}
|
||||
|
||||
bool InjectExternalCandidateSkin(
|
||||
std::wstring &html,
|
||||
const CandidateSkinCatalog::Package &skin,
|
||||
@@ -1596,12 +1720,19 @@ bool InjectExternalCandidateSkin(
|
||||
L"<style id=\"external-candidate-skin-vars\">:root{{--msime-skin-min-width:{}px;"
|
||||
L"--msime-skin-decoration-top:{}px;--msime-skin-decoration-width:{}px;}}</style>",
|
||||
skin.minWidthDip, skin.decorationTopDip, skin.decorationWidthDip);
|
||||
std::wstring css = BuildExternalCandidateSkinCss(skin, skinsRoot);
|
||||
NeutralizeEmbeddedStyleClosers(css);
|
||||
std::wstring generated;
|
||||
if (!css.empty())
|
||||
{
|
||||
generated = L"<style id=\"external-candidate-skin\">" + css + L"</style>";
|
||||
}
|
||||
const size_t headEnd = html.find(L"</head>");
|
||||
if (headEnd == std::wstring::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
html.insert(headEnd, vars);
|
||||
html.insert(headEnd, vars + generated);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1959,12 +2090,21 @@ std::wstring BuildCandidateSlotPayloadJson(const std::wstring &commaSeparated, b
|
||||
nlohmann::json payload;
|
||||
payload["preedit"] = words.empty() ? std::string{} : wstring_to_string(words[0]);
|
||||
payload["items"] = nlohmann::json::array();
|
||||
// Preserve slot indices: do not compress out empty tokens.
|
||||
// The WebView DOM renderer expects wrapper<->slot index alignment.
|
||||
uint16_t nonEmptyMask = 0;
|
||||
for (size_t i = 1; i < words.size(); ++i)
|
||||
{
|
||||
if (!words[i].empty())
|
||||
if (i <= 9 && !words[i].empty())
|
||||
{
|
||||
payload["items"].push_back(wstring_to_string(words[i]));
|
||||
nonEmptyMask |= static_cast<uint16_t>(1u) << static_cast<uint16_t>(i - 1);
|
||||
}
|
||||
payload["items"].push_back(wstring_to_string(words[i]));
|
||||
}
|
||||
if (nonEmptyMask == 0 && !words.empty())
|
||||
{
|
||||
CAND_DIAG_LOGF(L"candidate-slot payload all-empty words_sz={} content_only={} preedit_len={}",
|
||||
words.size(), contentOnly ? 1 : 0, words[0].size());
|
||||
}
|
||||
payload["selected"] = Global::candidate_ui.selected_index_in_page;
|
||||
payload["preeditVisible"] = GetConfiguredCandidateWindowPreeditStyle() != "empty";
|
||||
@@ -2525,18 +2665,20 @@ window.mouseBlockTimeout = setTimeout(() => {
|
||||
|
||||
void InflateCandWnd(std::wstring &str)
|
||||
{
|
||||
InflateCandWnd(str, nullptr, false);
|
||||
InflateCandWnd(str, nullptr);
|
||||
}
|
||||
|
||||
void InflateCandWnd(std::wstring &str, std::function<void()> onComplete)
|
||||
{
|
||||
InflateCandWnd(str, std::move(onComplete), false);
|
||||
std::wstring result = InflateCandidateTemplate(BodyStringCandWnd, str);
|
||||
UpdateHtmlContentWithJavaScript(webviewCandWnd, result, std::move(onComplete));
|
||||
}
|
||||
|
||||
void InflateCandWnd(std::wstring &str, std::function<void()> onComplete, bool contentOnly)
|
||||
{
|
||||
UpdateCandidateSlotsWithJavaScript(webviewCandWnd, BuildCandidateSlotPayloadJson(str, contentOnly),
|
||||
std::move(onComplete), contentOnly);
|
||||
(void)contentOnly;
|
||||
std::wstring result = InflateCandidateTemplate(BodyStringCandWnd, str);
|
||||
UpdateHtmlContentWithJavaScript(webviewCandWnd, result, std::move(onComplete));
|
||||
}
|
||||
|
||||
void InflateMeasureDivCandWnd(std::wstring &str)
|
||||
@@ -2702,6 +2844,10 @@ HRESULT OnControllerCreatedCandWnd( //
|
||||
(void)0;
|
||||
|
||||
// Navigate to HTML
|
||||
if (HTMLStringCandWnd.empty())
|
||||
{
|
||||
PrepareHtmlForWnds();
|
||||
}
|
||||
HRESULT hr = webviewCandWnd->NavigateToString(HTMLStringCandWnd.c_str());
|
||||
CAND_DIAG_LOGF(L"webview NavigateToString hr={:#x} html_chars={} skin={}", static_cast<unsigned>(hr),
|
||||
HTMLStringCandWnd.size(), string_to_wstring(preparedCandidateSkin));
|
||||
@@ -3618,6 +3764,14 @@ HRESULT OnControllerCreatedSettingsWnd( //
|
||||
PostSettingsConfig();
|
||||
}
|
||||
}
|
||||
else if (path == "appearance.ui_backend")
|
||||
{
|
||||
const std::string value = json::value_to<std::string>(data.at("value"));
|
||||
if (SetConfiguredUiBackend(value))
|
||||
{
|
||||
PostSettingsConfig();
|
||||
}
|
||||
}
|
||||
else if (path == "appearance.candidate_window_layout")
|
||||
{
|
||||
const std::string value = json::value_to<std::string>(data.at("value"));
|
||||
@@ -4375,7 +4529,8 @@ void PostSettingsConfig()
|
||||
{"y_mode", GetConfiguredYModeEnabled()},
|
||||
{"r_mode", GetConfiguredRModeEnabled()}}},
|
||||
{"appearance",
|
||||
{{"candidate_window_layout", GetConfiguredCandidateWindowLayout()},
|
||||
{{"ui_backend", GetConfiguredUiBackend()},
|
||||
{"candidate_window_layout", GetConfiguredCandidateWindowLayout()},
|
||||
{"candidate_window_follow_cursor", GetConfiguredCandidateWindowFollowCursor()},
|
||||
{"candidate_skin", GetConfiguredCandidateSkin()},
|
||||
{"candidate_window_preedit_style", GetConfiguredCandidateWindowPreeditStyle()},
|
||||
@@ -4780,8 +4935,8 @@ HRESULT OnFtbWindowEnvironmentCreated(HWND hwnd, HRESULT result, ICoreWebView2En
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the tray menu and floating toolbar WebViews in one
|
||||
* environment. The candidate window is Direct2D and does not use WebView2.
|
||||
* @brief Initialize candidate, tray menu, and floating toolbar WebViews in one
|
||||
* environment when appearance.ui_backend is webview2.
|
||||
*/
|
||||
void InitSmallWindowWebviews(HWND candHwnd, HWND menuHwnd, HWND ftbHwnd)
|
||||
{
|
||||
@@ -4794,9 +4949,10 @@ void InitSmallWindowWebviews(HWND candHwnd, HWND menuHwnd, HWND ftbHwnd)
|
||||
currentSmallWindowHostIndex = -1;
|
||||
lastFailedSmallWindowHostIndex = -1;
|
||||
pendingTrayMenuShow = false;
|
||||
if (FloatingToolbarPresenter::Instance().IsBound() && TrayMenuPresenter::Instance().IsBound())
|
||||
pendingCandidateShow = false;
|
||||
if (UseD2dSmallWindowUi())
|
||||
{
|
||||
FTB_DIAG_LOGF(L"skip small-window webview init: candidate/menu/ftb are d2d");
|
||||
FTB_DIAG_LOGF(L"skip small-window webview init: ui_backend=d2d");
|
||||
return;
|
||||
}
|
||||
BeginSmallWindowWebviewEnvironmentCreate();
|
||||
@@ -4851,6 +5007,7 @@ void ShutdownWebviews()
|
||||
// interface on the UI STA before WinMain balances CoInitializeEx.
|
||||
ResetSmallWindowTopmostGate();
|
||||
pendingTrayMenuShow = false;
|
||||
pendingCandidateShow = false;
|
||||
smallWindowInitState = SmallWindowInitState::Idle;
|
||||
smallWindowControllerRequestInFlight = false;
|
||||
if (smallWindowCandHwnd)
|
||||
|
||||
@@ -105,6 +105,13 @@ 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);
|
||||
|
||||
@@ -46,6 +46,10 @@ constexpr UINT_PTR TIMER_ID_FTB_VISIBILITY_RECONCILE = 9;
|
||||
constexpr UINT_PTR TIMER_ID_FTB_DPI_REMEASURE = 10;
|
||||
constexpr UINT_PTR TIMER_ID_CANDIDATE_MOVE_SETTLE = 11;
|
||||
constexpr UINT kCandidateMoveSettleMs = 50;
|
||||
// WebView2 backend: TSF may briefly toggle "should_show" while composing,
|
||||
// which can hide the candidate host right after it is un-cloaked.
|
||||
// Keep it visible for a short grace window so it can actually be perceived.
|
||||
constexpr UINT kCandidateHideGraceMs = 150;
|
||||
// Long enough fallback when the page never posts ready (old HTML / failed JS).
|
||||
// Page-ready normally ends the grace earlier; until then the toolbar stays shown.
|
||||
constexpr UINT kFloatingToolbarPaintGraceMs = 6000;
|
||||
@@ -69,6 +73,7 @@ std::atomic<bool> g_candidate_force_layout{false};
|
||||
std::atomic<uint64_t> g_candidate_content_generation{0};
|
||||
int g_last_placed_caret_x = Global::INVALID_Y;
|
||||
int g_last_placed_caret_y = Global::INVALID_Y;
|
||||
ULONGLONG g_candidate_hide_grace_until_tick = 0;
|
||||
bool g_candidate_session_anchor_valid = false;
|
||||
POINT g_candidate_session_anchor{};
|
||||
bool g_has_last_candidate_clip = false;
|
||||
@@ -419,7 +424,7 @@ void PlaceCandidateHostNearCaret(HWND hwnd, std::pair<double, double> requestedC
|
||||
KeepCandidateCardInsideHostAndMonitor(hostX, hostY, hostWidthPx, hostHeightPx, decoratedSize.first,
|
||||
decoratedSize.second, layoutScale, coordinates, cardSize.first);
|
||||
|
||||
UINT flag = SWP_NOZORDER | SWP_SHOWWINDOW;
|
||||
UINT flag = SWP_SHOWWINDOW;
|
||||
RECT currentRect{};
|
||||
if (GetWindowRect(hwnd, ¤tRect))
|
||||
{
|
||||
@@ -436,7 +441,7 @@ void PlaceCandidateHostNearCaret(HWND hwnd, std::pair<double, double> requestedC
|
||||
|
||||
{
|
||||
SuppressCandidateDpiChange suppressDpi;
|
||||
SetWindowPos(hwnd, nullptr, hostX, hostY, hostWidthPx, hostHeightPx, flag);
|
||||
SetWindowPos(hwnd, HWND_TOPMOST, hostX, hostY, hostWidthPx, hostHeightPx, flag);
|
||||
}
|
||||
if ((flag & SWP_NOSIZE) == 0)
|
||||
{
|
||||
@@ -1637,25 +1642,31 @@ int CreateCandidateWindow(HINSTANCE hInstance)
|
||||
#ifndef WS_EX_NOREDIRECTIONBITMAP
|
||||
#define WS_EX_NOREDIRECTIONBITMAP 0x00200000L
|
||||
#endif
|
||||
DWORD dwExStyle = WS_EX_NOREDIRECTIONBITMAP | //
|
||||
WS_EX_TOOLWINDOW | //
|
||||
WS_EX_NOACTIVATE; //
|
||||
const bool d2dUi = UseD2dSmallWindowUi();
|
||||
DWORD dwExStyle = d2dUi ? (WS_EX_NOREDIRECTIONBITMAP | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE)
|
||||
: (WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE);
|
||||
FLOAT scale = GetForegroundWindowScale();
|
||||
const int candWidth = d2dUi ? (std::max)(8, static_cast<int>(32 * scale))
|
||||
: static_cast<int>((::CANDIDATE_WINDOW_WIDTH + ::SHADOW_WIDTH + ::POP_UP_WND_WIDTH) *
|
||||
scale);
|
||||
const int candHeight = d2dUi ? (std::max)(8, static_cast<int>(32 * scale))
|
||||
: static_cast<int>(
|
||||
(::CANDIDATE_WINDOW_HEIGHT + ::SHADOW_HEIGHT + ::POP_UP_WND_HEIGHT) * scale);
|
||||
|
||||
HWND hwnd_cand = CreateWindowEx( //
|
||||
dwExStyle, //
|
||||
szWindowClass, //
|
||||
lpWindowNameCand, //
|
||||
WS_POPUP, //
|
||||
100, //
|
||||
100, //
|
||||
(std::max)(8, static_cast<int>(32 * scale)), //
|
||||
(std::max)(8, static_cast<int>(32 * scale)), //
|
||||
nullptr, //
|
||||
nullptr, //
|
||||
hInstance, //
|
||||
nullptr //
|
||||
); //
|
||||
HWND hwnd_cand = CreateWindowEx( //
|
||||
dwExStyle, //
|
||||
szWindowClass, //
|
||||
lpWindowNameCand, //
|
||||
WS_POPUP, //
|
||||
100, //
|
||||
100, //
|
||||
candWidth, //
|
||||
candHeight, //
|
||||
nullptr, //
|
||||
nullptr, //
|
||||
hInstance, //
|
||||
nullptr //
|
||||
); //
|
||||
|
||||
if (!hwnd_cand)
|
||||
{
|
||||
@@ -1672,8 +1683,16 @@ int CreateCandidateWindow(HINSTANCE hInstance)
|
||||
}
|
||||
|
||||
::global_hwnd = hwnd_cand;
|
||||
CandidatePresenter::Instance().Bind(hwnd_cand);
|
||||
CAND_DIAG_LOGF(L"candidate host created {}", DescribeCandidateHostState());
|
||||
if (d2dUi)
|
||||
{
|
||||
CandidatePresenter::Instance().Bind(hwnd_cand);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrepareLayeredHostWindow(hwnd_cand);
|
||||
}
|
||||
CAND_DIAG_LOGF(L"candidate host created backend={} {}", d2dUi ? L"d2d" : L"webview2",
|
||||
DescribeCandidateHostState());
|
||||
|
||||
// A client can activate while the pipe server is already listening but this
|
||||
// window does not exist yet, which is the race that leaves the toolbar
|
||||
@@ -1713,7 +1732,10 @@ int CreateCandidateWindow(HINSTANCE hInstance)
|
||||
}
|
||||
PrepareLayeredHostWindow(hwnd_menu);
|
||||
::global_hwnd_menu = hwnd_menu;
|
||||
TrayMenuPresenter::Instance().Bind(hwnd_menu);
|
||||
if (UseD2dSmallWindowUi())
|
||||
{
|
||||
TrayMenuPresenter::Instance().Bind(hwnd_menu);
|
||||
}
|
||||
|
||||
//
|
||||
// floating toolbar 窗口
|
||||
@@ -1755,7 +1777,10 @@ int CreateCandidateWindow(HINSTANCE hInstance)
|
||||
PrepareLayeredHostWindow(hwnd_ftb);
|
||||
::global_hwnd_ftb = hwnd_ftb;
|
||||
FanyNamedPipe::RegisterStatusSnapshotWindow(hwnd_ftb);
|
||||
FloatingToolbarPresenter::Instance().Bind(hwnd_ftb);
|
||||
if (UseD2dSmallWindowUi())
|
||||
{
|
||||
FloatingToolbarPresenter::Instance().Bind(hwnd_ftb);
|
||||
}
|
||||
|
||||
// Cloaked show: WebView2 sees a visible on-monitor host; the user does not.
|
||||
WarmupHostWindowCloaked(hwnd_cand);
|
||||
@@ -1770,14 +1795,17 @@ int CreateCandidateWindow(HINSTANCE hInstance)
|
||||
UpdateWindow(hwnd_ftb);
|
||||
|
||||
//
|
||||
// Preparing webview2 env
|
||||
// Preparing webview2 env (skipped when appearance.ui_backend is d2d)
|
||||
//
|
||||
PrepareHtmlForWnds();
|
||||
/* 候选框、托盘语言区右键菜单和 floating toolbar 共用一个 WebView2 environment */
|
||||
InitSmallWindowWebviews(hwnd_cand, hwnd_menu, hwnd_ftb);
|
||||
if (!UseD2dSmallWindowUi())
|
||||
{
|
||||
PrepareHtmlForWnds();
|
||||
/* 候选框、托盘语言区右键菜单和 floating toolbar 共用一个 WebView2 environment */
|
||||
InitSmallWindowWebviews(hwnd_cand, hwnd_menu, hwnd_ftb);
|
||||
|
||||
/* 菜单窗口:首屏导航完成后量一次尺寸(暂不 TOPMOST) */
|
||||
SetTimer(hwnd_menu, TIMER_ID_INIT_WEBVIEW_MENU, 200, nullptr);
|
||||
/* 菜单窗口:首屏导航完成后量一次尺寸(暂不 TOPMOST) */
|
||||
SetTimer(hwnd_menu, TIMER_ID_INIT_WEBVIEW_MENU, 200, nullptr);
|
||||
}
|
||||
|
||||
/* 监听文本配置文件变化,并同步到运行中的候选框。Settings 已是独立进程。 */
|
||||
SetTimer(hwnd_cand, TIMER_ID_CONFIG_SYNC, 300, nullptr);
|
||||
@@ -1908,11 +1936,94 @@ LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARA
|
||||
(void)0;
|
||||
}
|
||||
const POINT layoutCaret = GetCandidateLayoutCaret();
|
||||
CandidatePresenter::Instance().ShowFromGlobalState(layoutCaret);
|
||||
g_last_placed_caret_x = layoutCaret.x;
|
||||
g_last_placed_caret_y = layoutCaret.y;
|
||||
CAND_DIAG_LOGF(L"candidate-frame path=d2d content_gen={} elapsed_ms={}", contentGeneration,
|
||||
GetTickCount64() - updateStartedTick);
|
||||
if (CandidatePresenter::Instance().IsBound())
|
||||
{
|
||||
CandidatePresenter::Instance().ShowFromGlobalState(layoutCaret);
|
||||
g_last_placed_caret_x = layoutCaret.x;
|
||||
g_last_placed_caret_y = layoutCaret.y;
|
||||
CAND_DIAG_LOGF(L"candidate-frame path=d2d content_gen={} elapsed_ms={}", contentGeneration,
|
||||
GetTickCount64() - updateStartedTick);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Do not SetWindowPos / ExecuteScript the candidate host while its
|
||||
// WebView2 controller is still being created. That is what makes
|
||||
// CreateCoreWebView2Controller fail for this HWND while menu/FTB succeed.
|
||||
::is_global_wnd_cand_shown = true;
|
||||
if (!IsCandidateWebviewReady())
|
||||
{
|
||||
DeferCandidateShowUntilWebviewReady();
|
||||
CAND_DIAG_LOGF(L"candidate-frame path=deferred-webview content_gen={} {}", contentGeneration,
|
||||
DescribeCandidateHostState());
|
||||
return 0;
|
||||
}
|
||||
RaiseCandidateHostForShow(L"show-candidate");
|
||||
|
||||
std::wstring preedit =
|
||||
GetConfiguredCandidateWindowPreeditStyle() == "empty" ? std::wstring{} : GetPreeditWithCaretMarker();
|
||||
std::wstring str = preedit + L"," + Global::CandidateString;
|
||||
const bool sameCaret =
|
||||
g_last_placed_caret_x == layoutCaret.x && g_last_placed_caret_y == layoutCaret.y;
|
||||
const bool alreadyVisible = IsCandidateHostPaintedVisible(hwnd);
|
||||
const bool layoutInflight = g_candidate_layout_inflight.load();
|
||||
CAND_DIAG_LOGF(L"candidate-layout-decision content_gen={} last_caret=({},{}) caret=({},{}) "
|
||||
L"delta=({},{}) same={} visible={} inflight={}",
|
||||
contentGeneration, g_last_placed_caret_x, g_last_placed_caret_y, layoutCaret.x,
|
||||
layoutCaret.y, static_cast<long long>(layoutCaret.x) - g_last_placed_caret_x,
|
||||
static_cast<long long>(layoutCaret.y) - g_last_placed_caret_y, sameCaret, alreadyVisible,
|
||||
layoutInflight);
|
||||
if (!alreadyVisible)
|
||||
{
|
||||
PlaceCandidateHostNearCaret(hwnd);
|
||||
}
|
||||
if (alreadyVisible)
|
||||
{
|
||||
CAND_DIAG_LOGF(L"candidate-frame path=content-only content_gen={} inflight={}", contentGeneration,
|
||||
layoutInflight);
|
||||
InflateCandWnd(str, [hwnd, contentGeneration, updateStartedTick]() {
|
||||
const ULONGLONG callbackTick = GetTickCount64();
|
||||
CAND_DIAG_LOGF(L"candidate-frame dom-callback content_gen={} current_gen={} elapsed_ms={}",
|
||||
contentGeneration, g_candidate_content_generation.load(),
|
||||
callbackTick - updateStartedTick);
|
||||
if (!::is_global_wnd_cand_shown || contentGeneration != g_candidate_content_generation.load())
|
||||
{
|
||||
return;
|
||||
}
|
||||
RefreshCandidateClipAfterPaint(hwnd, contentGeneration, updateStartedTick);
|
||||
});
|
||||
if (!sameCaret)
|
||||
{
|
||||
if (CandidateCaretNeedsStableFollow(hwnd, layoutCaret))
|
||||
{
|
||||
KillTimer(hwnd, TIMER_ID_CANDIDATE_MOVE_SETTLE);
|
||||
SetTimer(hwnd, TIMER_ID_CANDIDATE_MOVE_SETTLE, kCandidateMoveSettleMs, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
CAND_DIAG_LOGF(L"candidate-position same-line-follow-suppressed anchor=({},{}) reported=({},{})",
|
||||
g_last_placed_caret_x, g_last_placed_caret_y, layoutCaret.x, layoutCaret.y);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (layoutInflight)
|
||||
{
|
||||
CAND_DIAG_LOGF(L"show folded into in-flight fine-tune caret=({},{})", Global::Point[0],
|
||||
Global::Point[1]);
|
||||
CAND_DIAG_LOGF(L"candidate-frame path=folded-inflight content_gen={}", contentGeneration);
|
||||
InflateCandWnd(str);
|
||||
KillTimer(hwnd, TIMER_ID_CANDIDATE_MOVE_SETTLE);
|
||||
SetTimer(hwnd, TIMER_ID_CANDIDATE_MOVE_SETTLE, kCandidateMoveSettleMs, nullptr);
|
||||
return 0;
|
||||
}
|
||||
CAND_DIAG_LOGF(L"candidate-frame path=full-layout content_gen={}", contentGeneration);
|
||||
InflateMeasureDivCandWnd(str, [hwnd]() {
|
||||
if (!::is_global_wnd_cand_shown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FineTuneWindow(hwnd);
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1931,7 +2042,36 @@ LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARA
|
||||
ResetCandidatePlacementMemory();
|
||||
{
|
||||
SuppressCandidateDpiChange suppressDpi;
|
||||
CandidatePresenter::Instance().Hide();
|
||||
if (CandidatePresenter::Instance().IsBound())
|
||||
{
|
||||
CandidatePresenter::Instance().Hide();
|
||||
}
|
||||
else if (!webviewControllerCandWnd)
|
||||
{
|
||||
// Moving this HWND while CreateCoreWebView2Controller is in
|
||||
// flight makes the candidate controller fail (menu/FTB still work).
|
||||
SetHostWindowCloaked(hwnd, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearCandidateWindowRegion(hwnd);
|
||||
FLOAT scale = GetForegroundWindowScale();
|
||||
if (scale < 1.5)
|
||||
{
|
||||
scale = 1.5;
|
||||
}
|
||||
SetWindowPos( //
|
||||
hwnd, //
|
||||
HWND_TOP, //
|
||||
0, //
|
||||
Global::INVALID_Y, //
|
||||
(::CANDIDATE_WINDOW_WIDTH + ::SHADOW_WIDTH + ::POP_UP_WND_WIDTH) * scale, //
|
||||
(::CANDIDATE_WINDOW_HEIGHT + ::SHADOW_HEIGHT + ::POP_UP_WND_HEIGHT) * scale, //
|
||||
SWP_SHOWWINDOW //
|
||||
);
|
||||
SetHostWindowCloaked(hwnd, true);
|
||||
UpdateHtmlContentWithJavaScript(webviewCandWnd, L"");
|
||||
}
|
||||
}
|
||||
CAND_DIAG_LOGF(L"hide message end {}", DescribeCandidateHostState());
|
||||
return 0;
|
||||
@@ -2051,7 +2191,14 @@ LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARA
|
||||
ForceReloadConfiguredCandidateSkin();
|
||||
if (::is_global_wnd_cand_shown)
|
||||
{
|
||||
CandidatePresenter::Instance().ShowFromGlobalState(GetCandidateLayoutCaret());
|
||||
if (CandidatePresenter::Instance().IsBound())
|
||||
{
|
||||
CandidatePresenter::Instance().ShowFromGlobalState(GetCandidateLayoutCaret());
|
||||
}
|
||||
else
|
||||
{
|
||||
FineTuneWindow(hwnd);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -2170,7 +2317,14 @@ LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARA
|
||||
previous_preedit_font_size != GetConfiguredCandidateWindowPreeditFontSize() ||
|
||||
previous_cand_text_color != GetConfiguredCandidateTextColor()))
|
||||
{
|
||||
CandidatePresenter::Instance().ShowFromGlobalState(GetCandidateLayoutCaret());
|
||||
if (CandidatePresenter::Instance().IsBound())
|
||||
{
|
||||
CandidatePresenter::Instance().ShowFromGlobalState(GetCandidateLayoutCaret());
|
||||
}
|
||||
else
|
||||
{
|
||||
FineTuneWindow(hwnd);
|
||||
}
|
||||
}
|
||||
if (previous_floating_toolbar != GetConfiguredFloatingToolbarEnabled())
|
||||
{
|
||||
@@ -2263,6 +2417,14 @@ LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARA
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_SIZE: {
|
||||
if (::webviewControllerCandWnd && wParam != SIZE_MINIMIZED)
|
||||
{
|
||||
SyncCandidateWebViewBoundsToHost(hwnd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear dictionary buffer cache */
|
||||
case WM_CLS_DICT_CACHE: {
|
||||
FanyNamedPipe::EnqueueResetInputSessionCacheTask();
|
||||
@@ -2505,6 +2667,10 @@ LRESULT CALLBACK WndProcMenuWindow(HWND hwnd, UINT message, WPARAM wParam, LPARA
|
||||
if (wParam == TIMER_ID_INIT_WEBVIEW_MENU)
|
||||
{
|
||||
KillTimer(hwnd, TIMER_ID_INIT_WEBVIEW_MENU);
|
||||
if (TrayMenuPresenter::Instance().IsBound() || UseD2dSmallWindowUi())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (::webviewMenuWnd) // 确保 webview 已初始化
|
||||
{
|
||||
GetContainerSizeMenu(webviewMenuWnd, [hwnd](std::pair<double, double> containerSize) {
|
||||
@@ -3210,7 +3376,11 @@ LRESULT CALLBACK WndProcFtbWindow(HWND hwnd, UINT message, WPARAM wParam, LPARAM
|
||||
if (wParam == TIMER_ID_FTB_DPI_REMEASURE)
|
||||
{
|
||||
KillTimer(hwnd, TIMER_ID_FTB_DPI_REMEASURE);
|
||||
if (::webviewFtbWnd)
|
||||
if (FloatingToolbarPresenter::Instance().IsBound())
|
||||
{
|
||||
FloatingToolbarPresenter::Instance().RelayoutHost();
|
||||
}
|
||||
else if (::webviewFtbWnd)
|
||||
{
|
||||
ApplyConfiguredFloatingToolbarSize();
|
||||
}
|
||||
@@ -3240,7 +3410,7 @@ int FineTuneWindow(HWND hwnd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
UINT flag = SWP_NOZORDER | SWP_SHOWWINDOW;
|
||||
UINT flag = SWP_SHOWWINDOW;
|
||||
|
||||
int caretX = layoutCaret.x;
|
||||
int caretY = layoutCaret.y;
|
||||
@@ -3250,7 +3420,7 @@ int FineTuneWindow(HWND hwnd)
|
||||
FLOAT scale = QueryCandidateHalfScreenDipLimitsForPoint(hwnd, caretPt).scale;
|
||||
|
||||
(void)0;
|
||||
if (!webviewCandWnd)
|
||||
if (!webviewCandWnd || !IsCandidateWebviewReady())
|
||||
{
|
||||
(void)0;
|
||||
LogSmallWindowReadyGate(L"fine-tune-no-webview");
|
||||
@@ -3446,7 +3616,7 @@ int FineTuneWindow(HWND hwnd)
|
||||
EndCandidateLayoutIfCurrent(generation);
|
||||
return;
|
||||
}
|
||||
|
||||
RaiseCandidateHostForShow(L"fine-tune");
|
||||
// Geometry size is stable (quarter-screen). Do not cloak — card motion
|
||||
// is CSS margin inside the already-placed transparent host.
|
||||
BOOL positioned = FALSE;
|
||||
@@ -3456,7 +3626,7 @@ int FineTuneWindow(HWND hwnd)
|
||||
SetLastError(0);
|
||||
positioned = SetWindowPos( //
|
||||
hwnd, //
|
||||
nullptr, //
|
||||
HWND_TOPMOST, //
|
||||
hostX, //
|
||||
hostY, //
|
||||
newWidth, //
|
||||
@@ -3525,7 +3695,7 @@ int FineTuneWindow(HWND hwnd)
|
||||
{
|
||||
SuppressCandidateDpiChange suppressDpi;
|
||||
SetLastError(0);
|
||||
const BOOL resyncResult = SetWindowPos(hwnd, nullptr, hostX, hostY, hostWidthPx, hostHeightPx, flag);
|
||||
const BOOL resyncResult = SetWindowPos(hwnd, HWND_TOPMOST, hostX, hostY, hostWidthPx, hostHeightPx, flag);
|
||||
WEBVIEW_DIAG_LOGF(L"ui-layout generation={} pass=dpi-resync scale={:.3f}->{:.3f} "
|
||||
L"desired=({},{},{}x{}) result={} gle={} half_dip=({:.2f},{:.2f}) margin=({},{})",
|
||||
generation, static_cast<double>(oldLayoutScale), static_cast<double>(layoutScale), hostX,
|
||||
@@ -3691,7 +3861,7 @@ int FineTuneWindow(HWND hwnd)
|
||||
SuppressCandidateDpiChange suppressDpi;
|
||||
// Prefer SWP_NOSIZE when already on the quarter-screen size so
|
||||
// only the clamped top-left moves with the card.
|
||||
UINT pass2Flag = SWP_NOZORDER | SWP_SHOWWINDOW;
|
||||
UINT pass2Flag = SWP_SHOWWINDOW;
|
||||
RECT cur{};
|
||||
if (GetWindowRect(hwnd, &cur) && (cur.right - cur.left) == hostWidthPx &&
|
||||
(cur.bottom - cur.top) == hostHeightPx)
|
||||
@@ -3699,7 +3869,7 @@ int FineTuneWindow(HWND hwnd)
|
||||
pass2Flag |= SWP_NOSIZE;
|
||||
}
|
||||
const BOOL pass2Result =
|
||||
SetWindowPos(hwnd, nullptr, hostX, hostY, hostWidthPx, hostHeightPx, pass2Flag);
|
||||
SetWindowPos(hwnd, HWND_TOPMOST, hostX, hostY, hostWidthPx, hostHeightPx, pass2Flag);
|
||||
CAND_DIAG_LOGF(L"candidate-layout painted-pass layout_gen={} caret=({},{}) "
|
||||
L"current=({},{},{}x{}) desired=({},{},{}x{}) flags={:#x} "
|
||||
L"result={} gle={} margin=({},{}) size_dip=({:.1f},{:.1f})",
|
||||
|
||||
Reference in new issue
Block a user