Add default IME language and per-app/global CN-EN scope settings.

Wire config + settings UI for default_ime_mode / ime_mode_scope, and sync unified mode across apps when scope is global.
This commit is contained in:
fanlusky committed 2026-07-28 20:26:25 +08:00
1 parent 71fa7dddeb
commit 6632e9ebcf
6 files changed
+151 -2

No files matched your search

+4
View File
@@ -92,6 +92,10 @@ tsf_preedit_style = "raw"
#
[input]
character_set = "simplified" # 候选输出字形,可选:"simplified", "traditional"
# 启动/从其他输入法切换到本输入法时的默认中英文,可选:"chinese", "english"
default_ime_mode = "chinese"
# 中英文状态范围,可选:"app"(按应用分别记忆), "global"(所有应用保持统一)
ime_mode_scope = "app"
# 组合说明:
# legacy + shuangpin -> 使用旧版双拼会话实现(当前最稳定)
# legacy + quanpin -> 自动切换为新版引擎全拼会话
+55
View File
@@ -17,6 +17,8 @@ namespace
std::string g_session_backend = "legacy";
SchemeType g_input_scheme = SchemeType::Shuangpin;
std::string g_character_set = "simplified";
std::string g_default_ime_mode = "chinese";
std::string g_ime_mode_scope = "app";
int g_candidate_page_size = 8;
std::string g_candidate_font = "Noto Sans SC";
std::string g_candidate_english_font = "Segoe UI";
@@ -282,6 +284,16 @@ bool LoadImeConfig()
const std::string character_set =
tbl["input"]["character_set"].value_or(std::string("simplified"));
g_character_set = character_set == "traditional" ? "traditional" : "simplified";
{
const std::string mode =
tbl["input"]["default_ime_mode"].value_or(std::string("chinese"));
g_default_ime_mode = mode == "english" ? "english" : "chinese";
}
{
const std::string scope =
tbl["input"]["ime_mode_scope"].value_or(std::string("app"));
g_ime_mode_scope = scope == "global" ? "global" : "app";
}
g_shuangpin_schema = tbl["input"]["shuangpin_schema"].value_or(std::string("xiaohe"));
g_wubi_schema = tbl["input"]["wubi_schema"].value_or(std::string("wubi86"));
g_shuangpin_preedit_mode = tbl["input"]["shuangpin_preedit_mode"].value_or(std::string("quanpin"));
@@ -751,6 +763,49 @@ bool SetConfiguredCharacterSet(const std::string &character_set)
return true;
}
const std::string &GetConfiguredDefaultImeMode()
{
return g_default_ime_mode;
}
bool SetConfiguredDefaultImeMode(const std::string &mode)
{
if (mode != "chinese" && mode != "english")
{
return false;
}
if (!WriteConfiguredValue("input", "default_ime_mode", EscapeTomlBasicString(mode)))
{
return false;
}
g_default_ime_mode = mode;
return true;
}
const std::string &GetConfiguredImeModeScope()
{
return g_ime_mode_scope;
}
bool SetConfiguredImeModeScope(const std::string &scope)
{
if (scope != "app" && scope != "global")
{
return false;
}
if (!WriteConfiguredValue("input", "ime_mode_scope", EscapeTomlBasicString(scope)))
{
return false;
}
g_ime_mode_scope = scope;
return true;
}
bool IsConfiguredImeModeScopeGlobal()
{
return g_ime_mode_scope == "global";
}
const std::string &GetConfiguredShuangpinSchema()
{
return g_shuangpin_schema;
+7
View File
@@ -81,6 +81,13 @@ std::string GetConfiguredInputSchemeName();
bool SetConfiguredInputScheme(const std::string &scheme);
const std::string &GetConfiguredCharacterSet();
bool SetConfiguredCharacterSet(const std::string &character_set);
// "chinese" | "english" — default CN/EN when activating this IME.
const std::string &GetConfiguredDefaultImeMode();
bool SetConfiguredDefaultImeMode(const std::string &mode);
// "app" | "global" — per-app memory vs unified CN/EN across apps.
const std::string &GetConfiguredImeModeScope();
bool SetConfiguredImeModeScope(const std::string &scope);
bool IsConfiguredImeModeScopeGlobal();
const std::string &GetConfiguredShuangpinSchema();
bool SetConfiguredShuangpinSchema(const std::string &schema);
const std::string &GetConfiguredWubiSchema();
+61 -2
View File
@@ -146,6 +146,13 @@ AsyncRequestOrigin g_ai_request_origin;
std::string g_ai_context;
std::mutex g_status_snapshot_mutex;
int g_latest_status_snapshot = -1;
// Global CN/EN authority for input.ime_mode_scope = "global".
// -1 until first StatusSnapshot or lazy seed from default_ime_mode.
int g_authoritative_cn_mode = -1;
uint64_t g_last_status_snapshot_client_id = 0;
// After switching away from this IME, the next StatusSnapshot must restore the
// global mode even when the same process/thread client_id reconnects.
bool g_force_global_ime_sync = false;
HWND g_status_snapshot_window = nullptr;
std::mutex g_candidate_ui_owner_mutex;
FanyImeIpc::CandidateUiOwnerState g_candidate_ui_owner;
@@ -184,6 +191,16 @@ void PublishStatusSnapshotValue(int packed_state)
}
}
int EnsureAuthoritativeCnMode()
{
if (g_authoritative_cn_mode < 0)
{
ReloadImeConfigIfChanged();
g_authoritative_cn_mode = GetConfiguredDefaultImeMode() == "english" ? 0 : 1;
}
return g_authoritative_cn_mode;
}
void UpdateCloudInput(const std::string &input, uint64_t client_id = 0, uint64_t activation_epoch = 0)
{
std::lock_guard lock(g_async_request_mutex);
@@ -944,6 +961,7 @@ void WorkerThread()
case TaskType::ClientDeactivated: {
// Unlike a route-only suspension, terminal TIP deactivation means
// the user switched to another input method.
g_force_global_ime_sync = true;
PostMessage(::global_hwnd, WM_IMEDEACTIVATE, 0, 0);
PostMessage(::global_hwnd, WM_HIDE_MAIN_WINDOW, 0, 0);
ClearState();
@@ -960,11 +978,52 @@ void WorkerThread()
}
case TaskType::StatusSnapshot: {
ReloadImeConfigIfChanged();
const int cn_state = task.pipe_data.keycode != 0 ? 1 : 0;
const int fullwidth_state = task.pipe_data.modifiers_down != 0 ? 1 : 0;
const int punctuation_state = task.pipe_data.pinyin_length != 0 ? 1 : 0;
const int packed_state = (cn_state << 2) | (fullwidth_state << 1) | punctuation_state;
if (FanyImeIpc::ShouldResetCompositionForImeMode(cn_state != 0))
int effective_cn = cn_state;
const bool force_global_sync = g_force_global_ime_sync;
g_force_global_ime_sync = false;
if (IsConfiguredImeModeScopeGlobal())
{
const int authoritative = EnsureAuthoritativeCnMode();
const bool client_changed =
g_last_status_snapshot_client_id != 0 && task.client_id != g_last_status_snapshot_client_id;
if (client_changed || force_global_sync)
{
// Focus moved to another app, or switched back to this IME:
// keep the unified mode.
effective_cn = authoritative;
if (cn_state != authoritative && task.client_id != 0)
{
SendToTsfWorkerThreadClientViaNamedpipe(
task.client_id, task.activation_epoch,
authoritative != 0 ? Global::DataFromServerMsgTypeToTsfWorkerThread::SwitchToCn
: Global::DataFromServerMsgTypeToTsfWorkerThread::SwitchToEn,
L"");
}
}
else
{
// Same client (or first report): accept as the new authority.
g_authoritative_cn_mode = cn_state;
effective_cn = cn_state;
}
}
else
{
g_authoritative_cn_mode = cn_state;
}
if (task.client_id != 0)
{
g_last_status_snapshot_client_id = task.client_id;
}
const int packed_state = (effective_cn << 2) | (fullwidth_state << 1) | punctuation_state;
if (FanyImeIpc::ShouldResetCompositionForImeMode(effective_cn != 0))
{
PostMessage(::global_hwnd, WM_HIDE_MAIN_WINDOW, 0, 0);
ClearState();
+6
View File
@@ -216,6 +216,8 @@ void PostConfig()
{{"input",
{{"schema", GetConfiguredInputSchemeName()},
{"character_set", GetConfiguredCharacterSet()},
{"default_ime_mode", GetConfiguredDefaultImeMode()},
{"ime_mode_scope", GetConfiguredImeModeScope()},
{"shuangpin_schema", GetConfiguredShuangpinSchema()},
{"wubi_schema", GetConfiguredWubiSchema()}}},
{"general",
@@ -306,6 +308,10 @@ bool ApplyConfigUpdate(const json::object &data)
return SetConfiguredInputScheme(json::value_to<std::string>(data.at("value")));
if (path == "input.character_set")
return SetConfiguredCharacterSet(json::value_to<std::string>(data.at("value")));
if (path == "input.default_ime_mode")
return SetConfiguredDefaultImeMode(json::value_to<std::string>(data.at("value")));
if (path == "input.ime_mode_scope")
return SetConfiguredImeModeScope(json::value_to<std::string>(data.at("value")));
if (path == "input.shuangpin_schema")
return SetConfiguredShuangpinSchema(json::value_to<std::string>(data.at("value")));
if (path == "input.wubi_schema")
+18
View File
@@ -1662,6 +1662,22 @@ HRESULT OnControllerCreatedSettingsWnd( //
PostSettingsConfig();
}
}
else if (path == "input.default_ime_mode")
{
const std::string value = json::value_to<std::string>(data.at("value"));
if (SetConfiguredDefaultImeMode(value))
{
PostSettingsConfig();
}
}
else if (path == "input.ime_mode_scope")
{
const std::string value = json::value_to<std::string>(data.at("value"));
if (SetConfiguredImeModeScope(value))
{
PostSettingsConfig();
}
}
else if (path == "input.shuangpin_schema")
{
const std::string value = json::value_to<std::string>(data.at("value"));
@@ -2087,6 +2103,8 @@ void PostSettingsConfig()
{"type", "configSnapshot"},
{"data", {{"input", {{"schema", GetConfiguredInputSchemeName()},
{"character_set", GetConfiguredCharacterSet()},
{"default_ime_mode", GetConfiguredDefaultImeMode()},
{"ime_mode_scope", GetConfiguredImeModeScope()},
{"shuangpin_schema", GetConfiguredShuangpinSchema()},
{"wubi_schema", GetConfiguredWubiSchema()}}},
{"general", {{"floating_toolbar", GetConfiguredFloatingToolbarEnabled()},