候选框开着且高亮候选带译文时,Ctrl+Enter 上屏那条译文而不是候选本身。 译文只有一条就直接上屏;有多条时把候选框整个换成这几条译义,空格/数字键 照常选一条上屏。 TSF 端原先在分类器最前面把所有 Ctrl 组合都还给应用,Ctrl+Enter 到不了 Server。现在在那条拦截之前先判一次:候选框开着才认,否则组合仍归应用。 认下来的键走既有的 FUNCTION_SERVER_CANDIDATE_KEY 通道,由 Server 决定 是上屏(CommitExactText)还是只开一个新候选页(NavigationIgnored)。 译文页复用同一份 candidate_ui.items,因此云/AI/英文/emoji/颜文字这些异步 回调和候选扩展都要在这个子模式下让路,否则会把译文冲掉。输入串全程没动, 退出时把存下来的 items 和页码原样放回去即可。译文页上的空格/数字键只回 Normal / OutofRange——SELECT_BY_NUMBER 的 TSF 路径只认这两种。
53 lines
2.5 KiB
C++
53 lines
2.5 KiB
C++
#include "ipc/candidate_translation_policy.h"
|
||
#include "tests/includes/test_framework.h"
|
||
|
||
TEST_CASE(ctrl_enter_alone_commits_the_secondary_candidate)
|
||
{
|
||
using FanyImeIpc::IsTranslationCommitKey;
|
||
REQUIRE(IsTranslationCommitKey(0x0D, FanyImeIpc::kModifierControl));
|
||
// The UI-less flag lives outside the modifier mask and must not disarm the shortcut.
|
||
REQUIRE(IsTranslationCommitKey(0x0D, FanyImeIpc::kModifierControl | FanyImeIpc::kModifierUiLess));
|
||
|
||
REQUIRE(!IsTranslationCommitKey(0x0D, 0));
|
||
REQUIRE(!IsTranslationCommitKey(0x0D, FanyImeIpc::kModifierControl | FanyImeIpc::kModifierShift));
|
||
REQUIRE(!IsTranslationCommitKey(0x0D, FanyImeIpc::kModifierControl | FanyImeIpc::kModifierAlt));
|
||
REQUIRE(!IsTranslationCommitKey(0x20, FanyImeIpc::kModifierControl));
|
||
REQUIRE(!IsTranslationCommitKey('J', FanyImeIpc::kModifierControl));
|
||
}
|
||
|
||
TEST_CASE(translation_gloss_splits_on_both_semicolon_widths)
|
||
{
|
||
using FanyImeIpc::SplitTranslationGloss;
|
||
// en->zh glosses join senses with the fullwidth semicolon.
|
||
const auto apple = SplitTranslationGloss("苹果;家伙;[计]苹果公司");
|
||
REQUIRE_EQ(apple.size(), static_cast<size_t>(3));
|
||
REQUIRE_EQ(apple[0], std::string("苹果"));
|
||
REQUIRE_EQ(apple[1], std::string("家伙"));
|
||
REQUIRE_EQ(apple[2], std::string("[计]苹果公司"));
|
||
|
||
// zh->en glosses use "; " and the trailing space must not survive.
|
||
const auto chuan = SplitTranslationGloss("string; trail; chuan");
|
||
REQUIRE_EQ(chuan.size(), static_cast<size_t>(3));
|
||
REQUIRE_EQ(chuan[0], std::string("string"));
|
||
REQUIRE_EQ(chuan[1], std::string("trail"));
|
||
REQUIRE_EQ(chuan[2], std::string("chuan"));
|
||
}
|
||
|
||
TEST_CASE(translation_gloss_keeps_a_single_sense_whole)
|
||
{
|
||
using FanyImeIpc::SplitTranslationGloss;
|
||
// A cloud gloss is one sentence: commas inside it are not sense separators.
|
||
const auto cloud = SplitTranslationGloss("今天天气不错,我们出去走走");
|
||
REQUIRE_EQ(cloud.size(), static_cast<size_t>(1));
|
||
REQUIRE_EQ(cloud[0], std::string("今天天气不错,我们出去走走"));
|
||
|
||
REQUIRE(SplitTranslationGloss("").empty());
|
||
REQUIRE(SplitTranslationGloss(" ").empty());
|
||
// Empty senses are dropped rather than becoming blank candidates.
|
||
REQUIRE_EQ(SplitTranslationGloss(";;").size(), static_cast<size_t>(0));
|
||
const auto ragged = SplitTranslationGloss(";apple; ;pear;");
|
||
REQUIRE_EQ(ragged.size(), static_cast<size_t>(2));
|
||
REQUIRE_EQ(ragged[0], std::string("apple"));
|
||
REQUIRE_EQ(ragged[1], std::string("pear"));
|
||
}
|