Files
Jia Suiandfanlusky 456b23ed94 feat(engine,server): 五笔支持 T 模式与 V 模式 (#942)
* 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>
2026-10-15 02:08:36 +08:00

70 lines
3.6 KiB
C++

#pragma once
#include "input_scheme.h"
#include <string>
#include <vector>
class WubiScheme : public IInputScheme
{
public:
void reset() override;
void handle_key(ImeKeyCode vk, ImeModifierMask modifiers_down, ImeCharacter wch) override;
QueryRequest build_request() const override;
std::string get_preedit() const override;
SchemeType type() const override;
void set_raw_input(const std::string &raw_input, const std::string &raw_input_with_cases) override;
// A wubi code is four letters, and a fifth is normally refused. Mixed input lifts that only
// once the table has failed the code in hand, so a spelling long enough to need it -- nihao,
// women, zhongguo -- can be finished. A code the table answers keeps the four-letter limit and
// a fluent wubi typist sees no change.
void set_extended_length_allowed(bool allowed);
// No wubi code uses z, so the key is normally dropped. Mixed input has to accept it, because z
// is an ordinary pinyin letter: dropping it does not refuse the spelling, it silently turns it
// into a different one -- zhongguo would arrive as hongguo and zi as i. Unlike the length, this
// follows the setting rather than the last query, since the letter can open a composition.
void set_mixed_pinyin_allowed(bool allowed);
// The wildcard mode wants the same key for the opposite reason: no code contains z, so a z
// stands for a letter the typist does not know. Same acceptance, different query -- see
// QueryRequest::wubi_z_wildcard. Independent of mixed input: a host can enable either, both or
// neither.
void set_z_wildcard(bool enabled);
// The host says the next raw string is a special mode's literal input (T mode's Tdate,
// V mode's V1+2), not a code. The raw string with cases is then kept exactly as typed, and the
// code derived from it is never complete and never queried: a mode's keyword can normalize to a
// real code (Tdate -> tdat), and treating that as wubi would commit a word and end the mode.
// Cleared by reset(); a host that drives the session by raw string sets it with every string.
void set_literal_input(bool literal);
bool literal_input() const
{
return literal_input_;
}
// A complete wubi code is the four letters the table is indexed by. Mixed input only lifts the
// length limit after the table has already failed the code in hand, so "complete" stays the
// fixed four letters rather than max_code_length() (32 while extended input is on): a longer
// spelling is a pinyin answer and is never a complete wubi code.
bool has_complete_code() const
{
return !literal_input_ && raw_input_.size() == kMaxCodeLength;
}
private:
static constexpr size_t kMaxCodeLength = 4;
// Long enough for any spelling, short enough to stay a composition rather than a paragraph.
static constexpr size_t kMaxMixedCodeLength = 32;
size_t max_code_length() const;
// For a code: the code letters as typed, cases kept, so lowercasing it gives raw_input_ exactly
// -- same letters, same length, same clipping. A host that counts this string to find the fourth
// letter counts code letters. For literal input: the string as typed, so a host reading it back
// still sees the uppercase T or V prefix and the digits its mode decisions match on. Queries
// never see it: they use raw_input_, the normalized code below.
std::string raw_input_with_cases_;
std::string raw_input_;
std::vector<KeyStroke> key_strokes_;
bool extended_length_allowed_ = false;
bool mixed_pinyin_allowed_ = false;
bool z_wildcard_ = false;
bool literal_input_ = false;
};