* feat(engine,server): 五笔支持 T 模式与 V 模式 五笔方案此前被排除在 Shift+T 日期时间模式与 V 模式之外。根因是方案层就把 raw 归一成小写 a–y、丢弃数字与运算符,Server 的形状判定读不到大写 T/V 前缀。 - 引擎 WubiScheme 新增 raw_input_with_cases_:保留大小写与 T/V 模式的非字母键 (数字、运算符、: /),但按码字域丢掉越域字母(默认配置下 z)。查询码仍由 normalize_wubi_code 派生,普通码下两者长度相等——Server 用原样串长度数四码。 - Server:T 触发新增独立谓词 date_time_scheme(chinese_scheme || Wubi),不并入 chinese_scheme 本身;FormatVModeWorkerPayload 对五笔返回 UppercaseOnly。 - 自动上屏 / 顶字两个谓词新增 special_mode_active 前置:T 模式关键词归一后可能与 合法四码重合(Tdate → tdat),不挡会在第四个字母上结束模式。 - 引擎与 Server 测试、README 与两份配置模板文案同步。 * fix(engine,server,ui-html): 五笔 T/V 模式由宿主标注字面输入 原实现让五笔原样串不再裁到码长,普通码下原样串可以比码长(混输关、z 通配时 wqazb 对 wqaz), Server 读回后光标、退格和上屏都会错位;守卫也只认 T 模式的全小写主体,TdatE 仍会顶出 tdat。 - 引擎:InputSession::set_wubi_literal_input 随 pending 序列一起下发,只作用于同一次设置。 普通码的原样串就是带大小写的码,与查询码逐位对应、一起截断;字面输入原样保留, 不查码表与拼音,也不算完整四码。退格从原样串重新派生码,字母键两份视图取同一个字母。 - Server:ApplyCompositionEditKey 按 IsWubiLiteralComposition(T 模式触发标记或 V 模式形状) 标注字面输入;两个五笔上屏策略恢复原签名,不再需要 special_mode_active。 - 设置页 T/V 模式说明补上五笔。 --------- Co-authored-by: fanlusky <1730976608@qq.com>
162 lines
5.2 KiB
C++
162 lines
5.2 KiB
C++
#include "wubi_scheme.h"
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
|
|
namespace
|
|
{
|
|
bool is_wubi_letter(char lower, bool mixed_pinyin_allowed, bool z_wildcard)
|
|
{
|
|
// Both flags exist to let z in: mixed input because z is a pinyin letter, wildcard because
|
|
// z stands for a code letter nobody knows. They are independent settings; accepting either
|
|
// is the same one line either way.
|
|
return (lower >= 'a' && lower <= 'y') || (lower == 'z' && (mixed_pinyin_allowed || z_wildcard));
|
|
}
|
|
|
|
// The code letters of input with their case kept, clipped to max_length: anything that is not a
|
|
// code letter (a z with mixed input and wildcard off, a digit, an operator) is dropped.
|
|
std::string wubi_code_letters(const std::string &input, size_t max_length, bool mixed_pinyin_allowed, bool z_wildcard)
|
|
{
|
|
std::string letters;
|
|
letters.reserve((std::min)(input.size(), max_length));
|
|
for (const unsigned char ch : input)
|
|
{
|
|
if (letters.size() == max_length)
|
|
{
|
|
break;
|
|
}
|
|
if (std::isalpha(ch) && is_wubi_letter(static_cast<char>(std::tolower(ch)), mixed_pinyin_allowed, z_wildcard))
|
|
{
|
|
letters.push_back(static_cast<char>(ch));
|
|
}
|
|
}
|
|
return letters;
|
|
}
|
|
|
|
std::string to_lower_ascii(std::string text)
|
|
{
|
|
for (char &ch : text)
|
|
{
|
|
ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
|
|
}
|
|
return text;
|
|
}
|
|
} // namespace
|
|
|
|
void WubiScheme::reset()
|
|
{
|
|
raw_input_with_cases_.clear();
|
|
raw_input_.clear();
|
|
key_strokes_.clear();
|
|
extended_length_allowed_ = false;
|
|
literal_input_ = false;
|
|
}
|
|
|
|
void WubiScheme::handle_key(ImeKeyCode vk, ImeModifierMask modifiers_down, ImeCharacter wch)
|
|
{
|
|
if (vk == ImeKey::Backspace)
|
|
{
|
|
if (!raw_input_with_cases_.empty())
|
|
{
|
|
raw_input_with_cases_.pop_back();
|
|
}
|
|
// Rederived rather than popped: in literal input the last character may be a digit or an
|
|
// operator that was never a code letter, and popping the code too would drift the two apart.
|
|
raw_input_ = to_lower_ascii(
|
|
wubi_code_letters(raw_input_with_cases_, max_code_length(), mixed_pinyin_allowed_, z_wildcard_));
|
|
if (!key_strokes_.empty())
|
|
{
|
|
key_strokes_.pop_back();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (vk == ImeKey::Escape || vk == ImeKey::Return)
|
|
{
|
|
reset();
|
|
return;
|
|
}
|
|
|
|
if (vk < 'A' || vk > 'Z')
|
|
{
|
|
return;
|
|
}
|
|
// Same rule as the Server's raw editing: the letter the key produced when it produced one,
|
|
// otherwise the key's own letter. Both views take the same letter, so they cannot disagree on
|
|
// a layout where the key and the character differ.
|
|
const bool wch_is_letter = (wch >= 'A' && wch <= 'Z') || (wch >= 'a' && wch <= 'z');
|
|
const char typed = wch_is_letter ? static_cast<char>(wch) : static_cast<char>(vk + ('a' - 'A'));
|
|
const char code_letter = static_cast<char>(std::tolower(static_cast<unsigned char>(typed)));
|
|
if (!is_wubi_letter(code_letter, mixed_pinyin_allowed_, z_wildcard_) || raw_input_.size() >= max_code_length())
|
|
{
|
|
return;
|
|
}
|
|
|
|
key_strokes_.push_back(KeyStroke{vk, modifiers_down, wch});
|
|
raw_input_.push_back(code_letter);
|
|
raw_input_with_cases_.push_back(typed);
|
|
}
|
|
|
|
void WubiScheme::set_raw_input(const std::string &raw_input, const std::string &raw_input_with_cases)
|
|
{
|
|
const std::string &typed = raw_input_with_cases.empty() ? raw_input : raw_input_with_cases;
|
|
raw_input_with_cases_ =
|
|
literal_input_ ? typed : wubi_code_letters(typed, max_code_length(), mixed_pinyin_allowed_, z_wildcard_);
|
|
raw_input_ =
|
|
to_lower_ascii(wubi_code_letters(raw_input_with_cases_, max_code_length(), mixed_pinyin_allowed_, z_wildcard_));
|
|
key_strokes_.clear();
|
|
}
|
|
|
|
void WubiScheme::set_literal_input(bool literal)
|
|
{
|
|
literal_input_ = literal;
|
|
}
|
|
|
|
void WubiScheme::set_extended_length_allowed(bool allowed)
|
|
{
|
|
extended_length_allowed_ = allowed;
|
|
}
|
|
|
|
void WubiScheme::set_mixed_pinyin_allowed(bool allowed)
|
|
{
|
|
mixed_pinyin_allowed_ = allowed;
|
|
}
|
|
|
|
void WubiScheme::set_z_wildcard(bool enabled)
|
|
{
|
|
z_wildcard_ = enabled;
|
|
}
|
|
|
|
size_t WubiScheme::max_code_length() const
|
|
{
|
|
return extended_length_allowed_ ? kMaxMixedCodeLength : kMaxCodeLength;
|
|
}
|
|
|
|
QueryRequest WubiScheme::build_request() const
|
|
{
|
|
QueryRequest request;
|
|
request.scheme = type();
|
|
request.raw_input = raw_input_;
|
|
request.raw_input_with_cases = raw_input_with_cases_;
|
|
request.normalized_input = raw_input_;
|
|
request.raw_segmentation = raw_input_;
|
|
request.normalized_segmentation = raw_input_;
|
|
request.segmentation = raw_input_;
|
|
request.key_strokes = key_strokes_;
|
|
// Wildcard mode only: the provider switches from a range scan to a pattern match when the
|
|
// code actually holds a z. Set here rather than in the provider so mixed input alone never
|
|
// reaches the pattern path; with both on, the session ranks pinyin ahead of the wildcard rows.
|
|
request.wubi_z_wildcard = z_wildcard_ && raw_input_.find('z') != std::string::npos;
|
|
request.valid = !raw_input_.empty();
|
|
return request;
|
|
}
|
|
|
|
std::string WubiScheme::get_preedit() const
|
|
{
|
|
return raw_input_;
|
|
}
|
|
|
|
SchemeType WubiScheme::type() const
|
|
{
|
|
return SchemeType::Wubi;
|
|
}
|