Add date-time quick input (T-mode) feature, strengthen installation to not overwrite user's config.toml, and add a cold-start protection for FTB that "displays within the page ready / up to 6s grace period, then shows/hides according to actual status" (still rebuilds webview2 on upgrade).
This commit is contained in:
1 parent
23d78f161a
commit
33a86c11b6
17 files changed
+829
-483
No files matched your search
@@ -4,6 +4,7 @@
|
||||
#include "defines/globals.h"
|
||||
#include "utils/common_utils.h"
|
||||
#include "utils/ime_utils.h"
|
||||
#include "window/floating_toolbar_visibility_policy.h"
|
||||
#include <debugapi.h>
|
||||
#include <boost/json.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
@@ -30,10 +31,12 @@ namespace json = boost::json;
|
||||
int FineTuneWindow(HWND hwnd);
|
||||
void ApplyConfiguredFloatingToolbarVisibility(const wchar_t *reason);
|
||||
void ApplyConfiguredFloatingToolbarSize();
|
||||
void ReconcileFloatingToolbarVisibilityAfterReady(const wchar_t *reason);
|
||||
void ApplyConfiguredInputScheme();
|
||||
void ApplyConfiguredShuangpinSchema();
|
||||
bool EnsureSmallWindowsTopmost(const wchar_t *reason);
|
||||
void UpdateSmallWindowWebviewVisibility(HWND hwnd, bool visible);
|
||||
void ClearFloatingToolbarNavigationState();
|
||||
std::wstring DescribeTrayMenuHostState();
|
||||
std::wstring GetAppdataPath();
|
||||
HRESULT OnEnvironmentCreated(HWND hwnd, HRESULT result, ICoreWebView2Environment *env);
|
||||
@@ -64,6 +67,11 @@ enum class SmallWindowInitState
|
||||
SmallWindowInitState smallWindowInitState = SmallWindowInitState::Idle;
|
||||
int smallWindowInitAttempts = 0;
|
||||
bool pendingTrayMenuShow = 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.
|
||||
bool floatingToolbarPaintGraceActive = false;
|
||||
bool floatingToolbarNavigationRetryUsed = false;
|
||||
// WebView2 rejects a CreateCoreWebView2Controller request with E_INVALIDARG when
|
||||
// another controller creation on the same environment is still in flight. The
|
||||
// three small windows must therefore be brought up strictly one at a time.
|
||||
@@ -75,7 +83,6 @@ constexpr UINT_PTR kRetrySmallWindowWebviewTimerId = 9001;
|
||||
|
||||
bool candidateNavigationReady = false;
|
||||
bool menuNavigationReady = false;
|
||||
bool floatingToolbarNavigationReady = false;
|
||||
bool smallWindowTopmostRequested = false;
|
||||
bool smallWindowTopmostApplied = false;
|
||||
// Guards against scheduling the staggered pin more than once.
|
||||
@@ -310,21 +317,21 @@ void BeginSmallWindowWebviewEnvironmentCreate()
|
||||
|
||||
const HRESULT createHr = CreateCoreWebView2EnvironmentWithOptions(
|
||||
nullptr, appDataPath.c_str(), options.Get(),
|
||||
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
|
||||
[](HRESULT result, ICoreWebView2Environment *env) -> HRESULT {
|
||||
if (FAILED(result) || !env)
|
||||
{
|
||||
OnSmallWindowWebviewInitFailed(FAILED(result) ? result : E_FAIL);
|
||||
return FAILED(result) ? result : E_FAIL;
|
||||
}
|
||||
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>([](HRESULT result,
|
||||
ICoreWebView2Environment *env)
|
||||
-> HRESULT {
|
||||
if (FAILED(result) || !env)
|
||||
{
|
||||
OnSmallWindowWebviewInitFailed(FAILED(result) ? result : E_FAIL);
|
||||
return FAILED(result) ? result : E_FAIL;
|
||||
}
|
||||
|
||||
smallWindowWebviewEnvironment = env;
|
||||
smallWindowInitState = SmallWindowInitState::Ready;
|
||||
smallWindowInitAttempts = 0;
|
||||
RequestNextSmallWindowController();
|
||||
return S_OK;
|
||||
})
|
||||
.Get());
|
||||
smallWindowWebviewEnvironment = env;
|
||||
smallWindowInitState = SmallWindowInitState::Ready;
|
||||
smallWindowInitAttempts = 0;
|
||||
RequestNextSmallWindowController();
|
||||
return S_OK;
|
||||
}).Get());
|
||||
|
||||
if (FAILED(createHr))
|
||||
{
|
||||
@@ -370,7 +377,7 @@ void ResetSmallWindowTopmostGate()
|
||||
CancelStaggeredTopmost();
|
||||
candidateNavigationReady = false;
|
||||
menuNavigationReady = false;
|
||||
floatingToolbarNavigationReady = false;
|
||||
ClearFloatingToolbarNavigationState();
|
||||
smallWindowTopmostRequested = false;
|
||||
smallWindowTopmostApplied = false;
|
||||
smallWindowTopmostScheduled = false;
|
||||
@@ -430,10 +437,9 @@ bool TrayMenuIsOpenToUser()
|
||||
|
||||
void ApplySmallWindowTopmostStep(SmallWindowTopmostStep step)
|
||||
{
|
||||
FTB_DIAG_LOGF(L"topmost step {} applying",
|
||||
step == SmallWindowTopmostStep::Candidate ? L"candidate"
|
||||
: step == SmallWindowTopmostStep::FloatingToolbar ? L"floating-toolbar"
|
||||
: L"tray-menu");
|
||||
FTB_DIAG_LOGF(L"topmost step {} applying", step == SmallWindowTopmostStep::Candidate ? L"candidate"
|
||||
: step == SmallWindowTopmostStep::FloatingToolbar ? L"floating-toolbar"
|
||||
: L"tray-menu");
|
||||
switch (step)
|
||||
{
|
||||
case SmallWindowTopmostStep::Candidate:
|
||||
@@ -473,8 +479,7 @@ void ApplySmallWindowTopmostStep(SmallWindowTopmostStep step)
|
||||
void CALLBACK SmallWindowTopmostTimerProc(HWND hwnd, UINT, UINT_PTR timerId, DWORD)
|
||||
{
|
||||
KillTimer(hwnd, timerId);
|
||||
ApplySmallWindowTopmostStep(
|
||||
static_cast<SmallWindowTopmostStep>(timerId - kTopmostStepTimerIdBase));
|
||||
ApplySmallWindowTopmostStep(static_cast<SmallWindowTopmostStep>(timerId - kTopmostStepTimerIdBase));
|
||||
}
|
||||
|
||||
// Returns false when there is no host window to hang the timers on, leaving the
|
||||
@@ -491,8 +496,7 @@ bool ScheduleStaggeredTopmost()
|
||||
UINT scheduled = 0;
|
||||
for (UINT_PTR step = 0; step < kTopmostStepCount; ++step)
|
||||
{
|
||||
if (SetTimer(timer_host, kTopmostStepTimerIdBase + step, delays[step],
|
||||
SmallWindowTopmostTimerProc) != 0)
|
||||
if (SetTimer(timer_host, kTopmostStepTimerIdBase + step, delays[step], SmallWindowTopmostTimerProc) != 0)
|
||||
{
|
||||
++scheduled;
|
||||
}
|
||||
@@ -533,8 +537,7 @@ void TryApplyPendingLazyTopmost(const wchar_t *reason)
|
||||
{
|
||||
FTB_DIAG_LOGF(L"topmost staggered from reason={}: candidate +{}ms, floating-toolbar +{}ms, "
|
||||
L"tray-menu +{}ms",
|
||||
reason, kCandidateTopmostDelayMs, kFloatingToolbarTopmostDelayMs,
|
||||
kTrayMenuTopmostDelayMs);
|
||||
reason, kCandidateTopmostDelayMs, kFloatingToolbarTopmostDelayMs, kTrayMenuTopmostDelayMs);
|
||||
return;
|
||||
}
|
||||
FTB_DIAG_LOGF(L"topmost timers unavailable for reason={}, pinning inline", reason);
|
||||
@@ -626,31 +629,26 @@ void RenderFloatingToolbarState(ICoreWebView2 *webview)
|
||||
const FloatingToolbarItemsConfig &items = GetConfiguredFloatingToolbarItems();
|
||||
script.append(L"window.setToolbarItem=(id,shown)=>{const item=document.getElementById(id);"
|
||||
L"if(item)item.style.display=shown?'flex':'none';};");
|
||||
script.append(items.fullwidth
|
||||
? L"window.setToolbarItem('char-width-mode',true);"
|
||||
: L"window.setToolbarItem('char-width-mode',false);");
|
||||
script.append(items.punctuation
|
||||
? L"window.setToolbarItem('punctuation-mode',true);"
|
||||
: L"window.setToolbarItem('punctuation-mode',false);");
|
||||
script.append(items.character_set
|
||||
? L"window.setToolbarItem('character-set',true);"
|
||||
: L"window.setToolbarItem('character-set',false);");
|
||||
script.append(items.emoji
|
||||
? L"window.setToolbarItem('emoji-panel',true);"
|
||||
: L"window.setToolbarItem('emoji-panel',false);");
|
||||
script.append(items.screen_keyboard
|
||||
? L"window.setToolbarItem('screen-keyboard',true);"
|
||||
: L"window.setToolbarItem('screen-keyboard',false);");
|
||||
script.append(items.settings
|
||||
? L"window.setToolbarItem('settings',true);"
|
||||
: L"window.setToolbarItem('settings',false);");
|
||||
script.append(items.fullwidth ? L"window.setToolbarItem('char-width-mode',true);"
|
||||
: L"window.setToolbarItem('char-width-mode',false);");
|
||||
script.append(items.punctuation ? L"window.setToolbarItem('punctuation-mode',true);"
|
||||
: L"window.setToolbarItem('punctuation-mode',false);");
|
||||
script.append(items.character_set ? L"window.setToolbarItem('character-set',true);"
|
||||
: L"window.setToolbarItem('character-set',false);");
|
||||
script.append(items.emoji ? L"window.setToolbarItem('emoji-panel',true);"
|
||||
: L"window.setToolbarItem('emoji-panel',false);");
|
||||
script.append(items.screen_keyboard ? L"window.setToolbarItem('screen-keyboard',true);"
|
||||
: L"window.setToolbarItem('screen-keyboard',false);");
|
||||
script.append(items.settings ? L"window.setToolbarItem('settings',true);"
|
||||
: L"window.setToolbarItem('settings',false);");
|
||||
|
||||
webview->ExecuteScript(script.c_str(), nullptr);
|
||||
}
|
||||
|
||||
void SetWebviewMemoryUsageTarget(ComPtr<ICoreWebView2> webview, COREWEBVIEW2_MEMORY_USAGE_TARGET_LEVEL level)
|
||||
{
|
||||
if (!webview) return;
|
||||
if (!webview)
|
||||
return;
|
||||
|
||||
ComPtr<ICoreWebView2_23> webview23;
|
||||
if (SUCCEEDED(webview.As(&webview23)))
|
||||
@@ -658,7 +656,7 @@ void SetWebviewMemoryUsageTarget(ComPtr<ICoreWebView2> webview, COREWEBVIEW2_MEM
|
||||
webview23->put_MemoryUsageTargetLevel(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool EnsureSmallWindowsTopmost(const wchar_t *reason)
|
||||
{
|
||||
@@ -700,8 +698,7 @@ void RaiseTrayMenuAboveSmallWindows(const wchar_t *reason)
|
||||
// Re-assert TOPMOST after FTB (or a peer) was pinned last. A second
|
||||
// HWND_TOPMOST is what actually lifts the menu within the topmost band;
|
||||
// HWND_TOP alone is unreliable here with WS_EX_NOACTIVATE layered hosts.
|
||||
FTB_DIAG_LOGF(L"menu raise reason={} nav_ready={} {}", reason, menuNavigationReady,
|
||||
DescribeTrayMenuHostState());
|
||||
FTB_DIAG_LOGF(L"menu raise reason={} nav_ready={} {}", reason, menuNavigationReady, DescribeTrayMenuHostState());
|
||||
constexpr UINT flag = SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE;
|
||||
SetLastError(0);
|
||||
if (!SetWindowPos(::global_hwnd_menu, HWND_TOPMOST, 0, 0, 0, 0, flag))
|
||||
@@ -731,6 +728,33 @@ bool IsFloatingToolbarWebviewReady()
|
||||
return floatingToolbarNavigationReady && webviewControllerFtbWnd != nullptr;
|
||||
}
|
||||
|
||||
bool IsFloatingToolbarPaintGraceActive()
|
||||
{
|
||||
return floatingToolbarPaintGraceActive;
|
||||
}
|
||||
|
||||
void BeginFloatingToolbarPaintGrace()
|
||||
{
|
||||
floatingToolbarPaintGraceActive = true;
|
||||
}
|
||||
|
||||
void EndFloatingToolbarPaintGrace()
|
||||
{
|
||||
floatingToolbarPaintGraceActive = false;
|
||||
}
|
||||
|
||||
void NotifyFloatingToolbarPageReady()
|
||||
{
|
||||
ReconcileFloatingToolbarVisibilityAfterReady(L"ftb-page-ready");
|
||||
}
|
||||
|
||||
void ClearFloatingToolbarNavigationState()
|
||||
{
|
||||
floatingToolbarNavigationReady = false;
|
||||
floatingToolbarPaintGraceActive = false;
|
||||
floatingToolbarNavigationRetryUsed = false;
|
||||
}
|
||||
|
||||
bool IsTrayMenuOpenToUser()
|
||||
{
|
||||
return TrayMenuIsOpenToUser();
|
||||
@@ -818,9 +842,8 @@ void UpdateSmallWindowWebviewVisibility(HWND hwnd, bool visible)
|
||||
|
||||
if (lowerMemoryWhenHidden)
|
||||
{
|
||||
SetWebviewMemoryUsageTarget(
|
||||
webview,
|
||||
visible ? COREWEBVIEW2_MEMORY_USAGE_TARGET_LEVEL_NORMAL : COREWEBVIEW2_MEMORY_USAGE_TARGET_LEVEL_LOW);
|
||||
SetWebviewMemoryUsageTarget(webview, visible ? COREWEBVIEW2_MEMORY_USAGE_TARGET_LEVEL_NORMAL
|
||||
: COREWEBVIEW2_MEMORY_USAGE_TARGET_LEVEL_LOW);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -910,13 +933,10 @@ void UpdateHtmlContentWithJavaScript(ComPtr<ICoreWebView2> webview, const std::w
|
||||
}
|
||||
|
||||
webview->ExecuteScript(
|
||||
script.c_str(),
|
||||
Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
|
||||
[onComplete](HRESULT, LPCWSTR) -> HRESULT {
|
||||
onComplete();
|
||||
return S_OK;
|
||||
})
|
||||
.Get());
|
||||
script.c_str(), Callback<ICoreWebView2ExecuteScriptCompletedHandler>([onComplete](HRESULT, LPCWSTR) -> HRESULT {
|
||||
onComplete();
|
||||
return S_OK;
|
||||
}).Get());
|
||||
}
|
||||
|
||||
void PrepareCandidateWebViewBoundsForMeasure(HWND hwnd)
|
||||
@@ -1012,11 +1032,9 @@ int PrepareHtmlForWnds()
|
||||
// floating toolbar 窗口
|
||||
//
|
||||
const bool ftbLight = ResolveConfiguredTheme(GetConfiguredThemeFtb()) == "light";
|
||||
std::wstring htmlFtbWnd =
|
||||
ftbLight ? L"/html/webview2/ftb/default_light.html" : L"/html/webview2/ftb/default.html";
|
||||
std::wstring htmlFtbWnd = ftbLight ? L"/html/webview2/ftb/default_light.html" : L"/html/webview2/ftb/default.html";
|
||||
std::wstring entireHtmlPathFtbWnd = assetPath + htmlFtbWnd;
|
||||
::HTMLStringFtbWnd =
|
||||
ReadHtmlFileWithFallback(entireHtmlPathFtbWnd, assetPath + L"/html/webview2/ftb/default.html");
|
||||
::HTMLStringFtbWnd = ReadHtmlFileWithFallback(entireHtmlPathFtbWnd, assetPath + L"/html/webview2/ftb/default.html");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1043,7 +1061,7 @@ bool ApplyConfiguredUiThemes()
|
||||
}
|
||||
if (webviewFtbWnd && !HTMLStringFtbWnd.empty())
|
||||
{
|
||||
floatingToolbarNavigationReady = false;
|
||||
ClearFloatingToolbarNavigationState();
|
||||
ok = SUCCEEDED(webviewFtbWnd->NavigateToString(HTMLStringFtbWnd.c_str())) && ok;
|
||||
}
|
||||
if (webviewMenuWnd && !HTMLStringMenuWnd.empty())
|
||||
@@ -1120,13 +1138,10 @@ void UpdateMeasureContentWithJavaScript(ComPtr<ICoreWebView2> webview, const std
|
||||
}
|
||||
|
||||
webview->ExecuteScript(
|
||||
script.c_str(),
|
||||
Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
|
||||
[onComplete](HRESULT, LPCWSTR) -> HRESULT {
|
||||
onComplete();
|
||||
return S_OK;
|
||||
})
|
||||
.Get());
|
||||
script.c_str(), Callback<ICoreWebView2ExecuteScriptCompletedHandler>([onComplete](HRESULT, LPCWSTR) -> HRESULT {
|
||||
onComplete();
|
||||
return S_OK;
|
||||
}).Get());
|
||||
}
|
||||
|
||||
void UpdateMeasureContentWithJavaScript(ComPtr<ICoreWebView2> webview, const std::wstring &newContent)
|
||||
@@ -1272,11 +1287,11 @@ HRESULT OnControllerCreatedCandWnd( //
|
||||
);
|
||||
|
||||
// Assets mapping
|
||||
const HRESULT mappingHr = webview3CandWnd->SetVirtualHostNameToFolderMapping( //
|
||||
L"candwnd", //
|
||||
assetPath.c_str(), //
|
||||
COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY_CORS //
|
||||
); //
|
||||
const HRESULT mappingHr = webview3CandWnd->SetVirtualHostNameToFolderMapping( //
|
||||
L"candwnd", //
|
||||
assetPath.c_str(), //
|
||||
COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY_CORS //
|
||||
); //
|
||||
(void)0;
|
||||
}
|
||||
|
||||
@@ -1367,10 +1382,8 @@ HRESULT OnControllerCreatedCandWnd( //
|
||||
const FLOAT scale = static_cast<FLOAT>(GetDpiForWindow(hwnd)) / 96.0f;
|
||||
const int current_width = static_cast<int>(window_rect.right - window_rect.left);
|
||||
const int current_height = static_cast<int>(window_rect.bottom - window_rect.top);
|
||||
const int top_expansion_px =
|
||||
static_cast<int>(std::ceil(top_expansion * scale));
|
||||
const int left_expansion_px =
|
||||
static_cast<int>(std::ceil(left_expansion * scale));
|
||||
const int top_expansion_px = static_cast<int>(std::ceil(top_expansion * scale));
|
||||
const int left_expansion_px = static_cast<int>(std::ceil(left_expansion * scale));
|
||||
SetWindowPos(hwnd, nullptr, window_rect.left - left_expansion_px,
|
||||
window_rect.top - top_expansion_px,
|
||||
(std::max)(current_width, static_cast<int>(std::ceil(width * scale))) +
|
||||
@@ -1388,20 +1401,20 @@ HRESULT OnControllerCreatedCandWnd( //
|
||||
webviewControllerCandWnd->put_Bounds(bounds);
|
||||
if (top_expansion > 0 || left_expansion > 0)
|
||||
{
|
||||
const std::wstring script =
|
||||
L"if(window.ApplyContextMenuTopExpansion)"
|
||||
L"window.ApplyContextMenuTopExpansion(" +
|
||||
std::to_wstring(top_expansion) +
|
||||
L");if(window.ApplyContextMenuLeftExpansion)"
|
||||
L"window.ApplyContextMenuLeftExpansion(" +
|
||||
std::to_wstring(left_expansion) + L");";
|
||||
const std::wstring script = L"if(window.ApplyContextMenuTopExpansion)"
|
||||
L"window.ApplyContextMenuTopExpansion(" +
|
||||
std::to_wstring(top_expansion) +
|
||||
L");if(window.ApplyContextMenuLeftExpansion)"
|
||||
L"window.ApplyContextMenuLeftExpansion(" +
|
||||
std::to_wstring(left_expansion) + L");";
|
||||
webviewCandWnd->ExecuteScript(script.c_str(), nullptr);
|
||||
}
|
||||
}
|
||||
else if (type == "contextMenuClosed")
|
||||
{
|
||||
const std::wstring preedit = GetConfiguredCandidateWindowPreeditStyle() == "empty"
|
||||
? std::wstring{} : GetPreeditWithCaretMarker();
|
||||
? std::wstring{}
|
||||
: GetPreeditWithCaretMarker();
|
||||
std::wstring measurement = preedit + L"," + Global::CandidateString;
|
||||
InflateMeasureDivCandWnd(measurement, [hwnd]() { FineTuneWindow(hwnd); });
|
||||
}
|
||||
@@ -1463,15 +1476,16 @@ HRESULT OnControllerCreatedCandWnd( //
|
||||
.Get(),
|
||||
nullptr);
|
||||
|
||||
webviewCandWnd->add_ProcessFailed(
|
||||
Microsoft::WRL::Callback<ICoreWebView2ProcessFailedEventHandler>(
|
||||
[](ICoreWebView2 *, ICoreWebView2ProcessFailedEventArgs *args) -> HRESULT {
|
||||
COREWEBVIEW2_PROCESS_FAILED_KIND kind = COREWEBVIEW2_PROCESS_FAILED_KIND_BROWSER_PROCESS_EXITED;
|
||||
const HRESULT hr = args->get_ProcessFailedKind(&kind);
|
||||
(void)0;
|
||||
return S_OK;
|
||||
}).Get(),
|
||||
nullptr);
|
||||
webviewCandWnd->add_ProcessFailed(Microsoft::WRL::Callback<ICoreWebView2ProcessFailedEventHandler>(
|
||||
[](ICoreWebView2 *, ICoreWebView2ProcessFailedEventArgs *args) -> HRESULT {
|
||||
COREWEBVIEW2_PROCESS_FAILED_KIND kind =
|
||||
COREWEBVIEW2_PROCESS_FAILED_KIND_BROWSER_PROCESS_EXITED;
|
||||
const HRESULT hr = args->get_ProcessFailedKind(&kind);
|
||||
(void)0;
|
||||
return S_OK;
|
||||
})
|
||||
.Get(),
|
||||
nullptr);
|
||||
|
||||
OnSmallWindowControllerSettled(S_OK);
|
||||
return S_OK;
|
||||
@@ -1495,7 +1509,7 @@ HRESULT OnEnvironmentCreated(HWND hwnd, HRESULT result, ICoreWebView2Environment
|
||||
}
|
||||
|
||||
// Create WebView2 controller
|
||||
const HRESULT hr = env->CreateCoreWebView2Controller( //
|
||||
const HRESULT hr = env->CreateCoreWebView2Controller( //
|
||||
hwnd, //
|
||||
Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>( //
|
||||
[hwnd](HRESULT result, //
|
||||
@@ -1621,8 +1635,7 @@ HRESULT OnControllerCreatedMenuWnd( //
|
||||
// Older menu assets already render the handwriting entry but do not
|
||||
// give it an id or native click bridge. Wire it at runtime so existing
|
||||
// installations gain the launcher without replacing their skin.
|
||||
sender->ExecuteScript(
|
||||
LR"((() => {
|
||||
sender->ExecuteScript(LR"((() => {
|
||||
const item = [...document.querySelectorAll('.menu-item')]
|
||||
.find(element => element.textContent.includes('\u624b\u5199\u8bc6\u522b\u677f'));
|
||||
if (item && !item.dataset.nativeHandwritingLauncher) {
|
||||
@@ -1632,7 +1645,7 @@ HRESULT OnControllerCreatedMenuWnd( //
|
||||
});
|
||||
}
|
||||
})())",
|
||||
nullptr);
|
||||
nullptr);
|
||||
SyncMenuFloatingToolbarToggle();
|
||||
PostMessage(hwnd, WM_REFRESH_MENU_SIZE, 0, 0);
|
||||
return S_OK;
|
||||
@@ -2282,8 +2295,7 @@ HRESULT OnControllerCreatedSettingsWnd( //
|
||||
}
|
||||
else if (path.rfind("general.floating_toolbar_", 0) == 0)
|
||||
{
|
||||
const std::string item =
|
||||
path.substr(std::string("general.floating_toolbar_").size());
|
||||
const std::string item = path.substr(std::string("general.floating_toolbar_").size());
|
||||
const bool value = json::value_to<bool>(data.at("value"));
|
||||
if (SetConfiguredFloatingToolbarItemEnabled(item, value))
|
||||
{
|
||||
@@ -2323,6 +2335,14 @@ HRESULT OnControllerCreatedSettingsWnd( //
|
||||
PostSettingsConfig();
|
||||
}
|
||||
}
|
||||
else if (path == "utility.date_time_mode")
|
||||
{
|
||||
const bool value = json::value_to<bool>(data.at("value"));
|
||||
if (SetConfiguredDateTimeModeEnabled(value))
|
||||
{
|
||||
PostSettingsConfig();
|
||||
}
|
||||
}
|
||||
else if (path == "general.paging_minus_equal")
|
||||
{
|
||||
const bool value = json::value_to<bool>(data.at("value"));
|
||||
@@ -2505,12 +2525,11 @@ void SyncMenuFloatingToolbarToggle()
|
||||
|
||||
// Keep the tray-menu toggle aligned with config.toml so Settings and tray
|
||||
// never drift apart when either side writes general.floating_toolbar.
|
||||
const wchar_t *script = GetConfiguredFloatingToolbarEnabled()
|
||||
? LR"((() => {
|
||||
const wchar_t *script = GetConfiguredFloatingToolbarEnabled() ? LR"((() => {
|
||||
const toggle = document.getElementById('floatingToggle');
|
||||
if (toggle) toggle.classList.add('active');
|
||||
})())"
|
||||
: LR"((() => {
|
||||
: LR"((() => {
|
||||
const toggle = document.getElementById('floatingToggle');
|
||||
if (toggle) toggle.classList.remove('active');
|
||||
})())";
|
||||
@@ -2545,67 +2564,69 @@ void PostSettingsConfig()
|
||||
const FloatingToolbarItemsConfig &toolbar = GetConfiguredFloatingToolbarItems();
|
||||
nlohmann::json payload = {
|
||||
{"type", "configSnapshot"},
|
||||
{"data", {{"input", {{"schema", GetConfiguredInputSchemeName()},
|
||||
{"character_set", GetConfiguredCharacterSet()},
|
||||
{"default_ime_mode", GetConfiguredDefaultImeMode()},
|
||||
{"ime_mode_scope", GetConfiguredImeModeScope()},
|
||||
{"shuangpin_schema", GetConfiguredShuangpinSchema()},
|
||||
{"wubi_schema", GetConfiguredWubiSchema()},
|
||||
{"word_to_character", GetConfiguredWordToCharacterEnabled()},
|
||||
{"smart_punctuation", GetConfiguredSmartPunctuationEnabled()}}},
|
||||
{"general", {{"floating_toolbar", GetConfiguredFloatingToolbarEnabled()},
|
||||
{"floating_toolbar_fullwidth", toolbar.fullwidth},
|
||||
{"floating_toolbar_punctuation", toolbar.punctuation},
|
||||
{"floating_toolbar_character_set", toolbar.character_set},
|
||||
{"floating_toolbar_emoji", toolbar.emoji},
|
||||
{"floating_toolbar_screen_keyboard", toolbar.screen_keyboard},
|
||||
{"floating_toolbar_settings", toolbar.settings},
|
||||
{"cn_en_mixed_input", GetConfiguredEnglishCandidatesEnabled()},
|
||||
{"cloud_candidates", GetConfiguredCloudCandidatesEnabled()},
|
||||
{"paging_minus_equal", GetConfiguredPagingMinusEqualEnabled()},
|
||||
{"paging_comma_period", GetConfiguredPagingCommaPeriodEnabled()},
|
||||
{"paging_tab", GetConfiguredPagingTabEnabled()},
|
||||
{"paging_page_up_down", GetConfiguredPagingPageUpDownEnabled()},
|
||||
{"candidate_arrow_navigation", GetConfiguredCandidateArrowNavigationEnabled()}}},
|
||||
{"keybindings", {{"switch_language_shift", GetConfiguredSwitchLanguageShiftEnabled()},
|
||||
{"switch_language_ctrl", GetConfiguredSwitchLanguageCtrlEnabled()},
|
||||
{"switch_language_ctrl_alt_space",
|
||||
GetConfiguredSwitchLanguageCtrlAltSpaceEnabled()}}},
|
||||
{"utility", {{"unicode_mode", GetConfiguredUnicodeModeEnabled()},
|
||||
{"quick_phrase", GetConfiguredQuickPhraseEnabled()}}},
|
||||
{"appearance", {{"candidate_window_layout", GetConfiguredCandidateWindowLayout()},
|
||||
{"candidate_window_preedit_style", GetConfiguredCandidateWindowPreeditStyle()},
|
||||
{"tsf_preedit_style", GetConfiguredTsfPreeditStyle()},
|
||||
{"theme_mode", GetConfiguredThemeMode()},
|
||||
{"theme_settings", GetConfiguredThemeSettings()},
|
||||
{"theme_cand", GetConfiguredThemeCand()},
|
||||
{"theme_ftb", GetConfiguredThemeFtb()},
|
||||
{"theme_menu", GetConfiguredThemeMenu()},
|
||||
{"theme_emoji", GetConfiguredThemeEmoji()},
|
||||
{"theme_screen_keyboard", GetConfiguredThemeScreenKeyboard()},
|
||||
{"theme_handwriting", GetConfiguredThemeHandwriting()},
|
||||
{"theme_voice", GetConfiguredThemeVoice()},
|
||||
{"page_size", GetConfiguredCandidatePageSize()},
|
||||
{"font", GetConfiguredCandidateFont()},
|
||||
{"font_css_family", ResolveSystemFontFamilyForCss(GetConfiguredCandidateFont())},
|
||||
{"english_font", GetConfiguredCandidateEnglishFont()},
|
||||
{"english_font_css_family",
|
||||
ResolveSystemFontFamilyForCss(GetConfiguredCandidateEnglishFont())},
|
||||
{"default_font", GetConfiguredCandidateDefaultFont()},
|
||||
{"default_font_css_family",
|
||||
ResolveSystemFontFamilyForCss(GetConfiguredCandidateDefaultFont())},
|
||||
{"font_size", GetConfiguredCandidateFontSize()},
|
||||
{"cand_text_color", GetConfiguredCandidateTextColor()},
|
||||
{"system_fonts", GetSystemFontFamilies()}}},
|
||||
{"helpcode",
|
||||
{{"shuangpin_helpcode", GetConfiguredShuangpinHelpcodeEnabled()},
|
||||
{"shuangpin_helpcode_schema", GetConfiguredShuangpinHelpcodeSchema()},
|
||||
{"quanpin_helpcode", GetConfiguredQuanpinHelpcodeEnabled()},
|
||||
{"quanpin_helpcode_schema", GetConfiguredQuanpinHelpcodeSchema()},
|
||||
{"show_sp_helpcode_in_candidate_window",
|
||||
GetConfiguredShowShuangpinHelpcodeInCandidateWindow()},
|
||||
{"show_qp_helpcode_in_candidate_window",
|
||||
GetConfiguredShowQuanpinHelpcodeInCandidateWindow()}}}}}};
|
||||
{"data",
|
||||
{{"input",
|
||||
{{"schema", GetConfiguredInputSchemeName()},
|
||||
{"character_set", GetConfiguredCharacterSet()},
|
||||
{"default_ime_mode", GetConfiguredDefaultImeMode()},
|
||||
{"ime_mode_scope", GetConfiguredImeModeScope()},
|
||||
{"shuangpin_schema", GetConfiguredShuangpinSchema()},
|
||||
{"wubi_schema", GetConfiguredWubiSchema()},
|
||||
{"word_to_character", GetConfiguredWordToCharacterEnabled()},
|
||||
{"smart_punctuation", GetConfiguredSmartPunctuationEnabled()}}},
|
||||
{"general",
|
||||
{{"floating_toolbar", GetConfiguredFloatingToolbarEnabled()},
|
||||
{"floating_toolbar_fullwidth", toolbar.fullwidth},
|
||||
{"floating_toolbar_punctuation", toolbar.punctuation},
|
||||
{"floating_toolbar_character_set", toolbar.character_set},
|
||||
{"floating_toolbar_emoji", toolbar.emoji},
|
||||
{"floating_toolbar_screen_keyboard", toolbar.screen_keyboard},
|
||||
{"floating_toolbar_settings", toolbar.settings},
|
||||
{"cn_en_mixed_input", GetConfiguredEnglishCandidatesEnabled()},
|
||||
{"cloud_candidates", GetConfiguredCloudCandidatesEnabled()},
|
||||
{"paging_minus_equal", GetConfiguredPagingMinusEqualEnabled()},
|
||||
{"paging_comma_period", GetConfiguredPagingCommaPeriodEnabled()},
|
||||
{"paging_tab", GetConfiguredPagingTabEnabled()},
|
||||
{"paging_page_up_down", GetConfiguredPagingPageUpDownEnabled()},
|
||||
{"candidate_arrow_navigation", GetConfiguredCandidateArrowNavigationEnabled()}}},
|
||||
{"keybindings",
|
||||
{{"switch_language_shift", GetConfiguredSwitchLanguageShiftEnabled()},
|
||||
{"switch_language_ctrl", GetConfiguredSwitchLanguageCtrlEnabled()},
|
||||
{"switch_language_ctrl_alt_space", GetConfiguredSwitchLanguageCtrlAltSpaceEnabled()}}},
|
||||
{"utility",
|
||||
{{"unicode_mode", GetConfiguredUnicodeModeEnabled()},
|
||||
{"quick_phrase", GetConfiguredQuickPhraseEnabled()},
|
||||
{"date_time_mode", GetConfiguredDateTimeModeEnabled()}}},
|
||||
{"appearance",
|
||||
{{"candidate_window_layout", GetConfiguredCandidateWindowLayout()},
|
||||
{"candidate_window_preedit_style", GetConfiguredCandidateWindowPreeditStyle()},
|
||||
{"tsf_preedit_style", GetConfiguredTsfPreeditStyle()},
|
||||
{"theme_mode", GetConfiguredThemeMode()},
|
||||
{"theme_settings", GetConfiguredThemeSettings()},
|
||||
{"theme_cand", GetConfiguredThemeCand()},
|
||||
{"theme_ftb", GetConfiguredThemeFtb()},
|
||||
{"theme_menu", GetConfiguredThemeMenu()},
|
||||
{"theme_emoji", GetConfiguredThemeEmoji()},
|
||||
{"theme_screen_keyboard", GetConfiguredThemeScreenKeyboard()},
|
||||
{"theme_handwriting", GetConfiguredThemeHandwriting()},
|
||||
{"theme_voice", GetConfiguredThemeVoice()},
|
||||
{"page_size", GetConfiguredCandidatePageSize()},
|
||||
{"font", GetConfiguredCandidateFont()},
|
||||
{"font_css_family", ResolveSystemFontFamilyForCss(GetConfiguredCandidateFont())},
|
||||
{"english_font", GetConfiguredCandidateEnglishFont()},
|
||||
{"english_font_css_family", ResolveSystemFontFamilyForCss(GetConfiguredCandidateEnglishFont())},
|
||||
{"default_font", GetConfiguredCandidateDefaultFont()},
|
||||
{"default_font_css_family", ResolveSystemFontFamilyForCss(GetConfiguredCandidateDefaultFont())},
|
||||
{"font_size", GetConfiguredCandidateFontSize()},
|
||||
{"cand_text_color", GetConfiguredCandidateTextColor()},
|
||||
{"system_fonts", GetSystemFontFamilies()}}},
|
||||
{"helpcode",
|
||||
{{"shuangpin_helpcode", GetConfiguredShuangpinHelpcodeEnabled()},
|
||||
{"shuangpin_helpcode_schema", GetConfiguredShuangpinHelpcodeSchema()},
|
||||
{"quanpin_helpcode", GetConfiguredQuanpinHelpcodeEnabled()},
|
||||
{"quanpin_helpcode_schema", GetConfiguredQuanpinHelpcodeSchema()},
|
||||
{"show_sp_helpcode_in_candidate_window", GetConfiguredShowShuangpinHelpcodeInCandidateWindow()},
|
||||
{"show_qp_helpcode_in_candidate_window", GetConfiguredShowQuanpinHelpcodeInCandidateWindow()}}}}}};
|
||||
const std::wstring message = string_to_wstring(payload.dump());
|
||||
::webviewSettingsWnd->PostWebMessageAsJson(message.c_str());
|
||||
}
|
||||
@@ -2673,8 +2694,8 @@ HRESULT OnControllerCreatedFtbWnd( //
|
||||
// warmup state: visible to WebView2, invisible to the user.
|
||||
DWORD hostCloaked = 0;
|
||||
DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &hostCloaked, sizeof(hostCloaked));
|
||||
FTB_DIAG_LOGF(L"ftb controller created host_visible={} host_cloaked={}",
|
||||
IsWindowVisible(hwnd) != FALSE, hostCloaked != 0);
|
||||
FTB_DIAG_LOGF(L"ftb controller created host_visible={} host_cloaked={}", IsWindowVisible(hwnd) != FALSE,
|
||||
hostCloaked != 0);
|
||||
UpdateSmallWindowWebviewVisibility(hwnd, IsWindowVisible(hwnd) != FALSE);
|
||||
|
||||
// Configure webviewFtbWindow settings
|
||||
@@ -2735,7 +2756,7 @@ HRESULT OnControllerCreatedFtbWnd( //
|
||||
// State notifications can arrive before the controller exists or while
|
||||
// NavigateToString is still loading. Only render after a successful
|
||||
// navigation, then replay the complete cached state in one operation.
|
||||
floatingToolbarNavigationReady = false;
|
||||
ClearFloatingToolbarNavigationState();
|
||||
EventRegistrationToken navigationCompletedToken{};
|
||||
const HRESULT navigationCompletedResult = webviewFtbWnd->add_NavigationCompleted(
|
||||
Microsoft::WRL::Callback<ICoreWebView2NavigationCompletedEventHandler>(
|
||||
@@ -2750,13 +2771,27 @@ HRESULT OnControllerCreatedFtbWnd( //
|
||||
NotifySmallWindowNavigationReady(floatingToolbarNavigationReady, L"floating-toolbar");
|
||||
RenderFloatingToolbarState(sender);
|
||||
ApplyConfiguredFloatingToolbarSize();
|
||||
// Host is warmed up cloaked. Uncloak (or hide) only after
|
||||
// the first navigation, when there is something to paint.
|
||||
// Show first so a cold user-data folder can finish painting;
|
||||
// a short timer then hides or keeps it based on real state.
|
||||
ApplyConfiguredFloatingToolbarVisibility(L"ftb-navigation-completed");
|
||||
}
|
||||
else
|
||||
{
|
||||
(void)0;
|
||||
// Cold user-data bring-up can fail the first NavigateToString.
|
||||
// Retry once against the same controller instead of leaving
|
||||
// the host permanently cloaked with webview_ready=false.
|
||||
if (!floatingToolbarNavigationRetryUsed && !::HTMLStringFtbWnd.empty())
|
||||
{
|
||||
floatingToolbarNavigationRetryUsed = true;
|
||||
floatingToolbarNavigationReady = false;
|
||||
floatingToolbarPaintGraceActive = false;
|
||||
FTB_DIAG_LOGF(L"ftb navigation failed; retrying NavigateToString once");
|
||||
sender->NavigateToString(::HTMLStringFtbWnd.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
FTB_DIAG_LOGF(L"ftb navigation failed permanently after retry");
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
})
|
||||
@@ -2801,6 +2836,10 @@ HRESULT OnControllerCreatedFtbWnd( //
|
||||
ReleaseCapture();
|
||||
PostMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
|
||||
}
|
||||
else if (type == "ready")
|
||||
{
|
||||
NotifyFloatingToolbarPageReady();
|
||||
}
|
||||
else if (type == "changeIMEMode")
|
||||
{
|
||||
std::string mode = json::value_to<std::string>(val.at("data"));
|
||||
@@ -2810,16 +2849,16 @@ HRESULT OnControllerCreatedFtbWnd( //
|
||||
#ifdef FANY_DEBUG
|
||||
(void)0;
|
||||
#endif
|
||||
SendToTsfWorkerThreadViaNamedpipe(Global::DataFromServerMsgTypeToTsfWorkerThread::SwitchToCn,
|
||||
L"");
|
||||
SendToTsfWorkerThreadViaNamedpipe(
|
||||
Global::DataFromServerMsgTypeToTsfWorkerThread::SwitchToCn, L"");
|
||||
}
|
||||
else if (mode == "en") // Change to EN
|
||||
{
|
||||
#ifdef FANY_DEBUG
|
||||
(void)0;
|
||||
#endif
|
||||
SendToTsfWorkerThreadViaNamedpipe(Global::DataFromServerMsgTypeToTsfWorkerThread::SwitchToEn,
|
||||
L"");
|
||||
SendToTsfWorkerThreadViaNamedpipe(
|
||||
Global::DataFromServerMsgTypeToTsfWorkerThread::SwitchToEn, L"");
|
||||
}
|
||||
}
|
||||
else if (type == "changeCharMode")
|
||||
@@ -2864,9 +2903,8 @@ HRESULT OnControllerCreatedFtbWnd( //
|
||||
}
|
||||
else if (type == "changeCharacterSet")
|
||||
{
|
||||
const std::string next = GetConfiguredCharacterSet() == "traditional"
|
||||
? "simplified"
|
||||
: "traditional";
|
||||
const std::string next =
|
||||
GetConfiguredCharacterSet() == "traditional" ? "simplified" : "traditional";
|
||||
if (SetConfiguredCharacterSet(next))
|
||||
{
|
||||
RenderFloatingToolbarState(sender);
|
||||
@@ -2977,8 +3015,8 @@ bool PrepareTrayMenuWebviewForShow()
|
||||
return false;
|
||||
}
|
||||
|
||||
FTB_DIAG_LOGF(L"menu prepare: no controller yet, init_state={} attempts={}",
|
||||
static_cast<int>(smallWindowInitState), smallWindowInitAttempts);
|
||||
FTB_DIAG_LOGF(L"menu prepare: no controller yet, init_state={} attempts={}", static_cast<int>(smallWindowInitState),
|
||||
smallWindowInitAttempts);
|
||||
if (smallWindowInitState != SmallWindowInitState::InProgress)
|
||||
{
|
||||
// An explicit right-click is a fresh user intent: reset the attempt
|
||||
|
||||
Reference in new issue
Block a user