Files
qingjian/apps/windows/server/tests/engine_loop/status.rs
T
yenharvey 464c2599cd fix(windows): 中英模式所有应用共用一份,切换键可多选并适配系统 Ctrl+Space
- 中英模式存在 Server、所有应用共用一份(与搜狗一致):用户在某个应用里切了用 ModeChanged 报上去,
  激活、线程得到焦点、轮询时用 SyncMode 取回跟上;悬浮状态条上点「中 / 英」直接改这一份。
  之前各应用各记一份,后台线程激活时报的模式会盖掉前台的,新打开的程序偶尔一打开就是英文。
- 只在前台窗口属于本进程时轮询:后台进程会一直以为自己在前台,它们来取模式会让悬浮条在切走输入法后
  又冒出来;真在前台的应用有时收不到「得到焦点」,启动后悬浮条要等打开设置程序才出现。
- 切换键改成勾选、可多选:单击 Shift(缺省)/ 单击 Ctrl / Ctrl+Alt+Space,配置写成列表,老配置的单个值照读,
  ctrl+space 转成 ctrl+alt+space。
- 系统的「输入法/非输入法切换」(缺省 Ctrl+Space)不进勾选项但照微软拼音适配:它翻的输入法开关
  关 = 英文、开 = 中文;我们切模式时把开关写成一致,按一下就切;开关一变作废被截走 Space 的那次「单击 Ctrl」。
- 会话号改用线程 id:TSF 的 client id 各进程都是同样那几个值,不同应用在 Server 那边撞成同一个会话。
- 协议版本保持 7:一个版本周期里只升一次。
- 任务栏点中 / 英触发的 compartment 回调里不再回写转换模式:在它自己的 OnChange 里写会报 0x8000FFFF。
2026-09-23 16:29:51 +08:00

172 lines
5.8 KiB
Rust

//! 悬浮状态条随模式、双拼方案与开关变化。
use crate::support::*;
#[test]
fn status_bar_mode_click_changes_the_global_mode() {
let config = RouterConfig {
status_enabled: true,
..RouterConfig::default()
};
let mut router = router_with(config);
let recorder = RecordingStatus::default();
router.set_status_sink(Box::new(recorder.clone()));
router.handle(ClientMessage::ModeChanged {
session: SESSION,
english: false,
});
// 点「中」:状态条翻成「英」,之后每个 DLL 来取都拿到英文(全局一份,不是取一次就清)。
router.handle_status_event(StatusEvent::ToggleMode);
assert_eq!(recorder.calls().last(), Some(&Some("英".to_owned())));
assert_eq!(synced_mode(&mut router, SESSION), Some(true));
assert_eq!(synced_mode(&mut router, SESSION), Some(true));
assert_eq!(synced_mode(&mut router, SessionId(2)), Some(true));
}
#[test]
fn status_bar_mode_click_is_ignored_when_builtin_english_is_off() {
let config = RouterConfig {
status_enabled: true,
english_mode: false,
..RouterConfig::default()
};
let mut router = router_with(config);
let recorder = RecordingStatus::default();
router.set_status_sink(Box::new(recorder.clone()));
router.handle(ClientMessage::ModeChanged {
session: SESSION,
english: false,
});
assert_eq!(recorder.calls().last(), Some(&Some("中".to_owned())));
// 关掉内置英文模式:点「中」不翻成「英」,DLL 取到的也是中文(DLL 那边同样会拦)
router.handle_status_event(StatusEvent::ToggleMode);
assert_eq!(recorder.calls().last(), Some(&Some("中".to_owned())));
assert_eq!(synced_mode(&mut router, SESSION), Some(false));
router.handle(ClientMessage::ModeChanged {
session: SESSION,
english: true,
});
assert_eq!(synced_mode(&mut router, SESSION), Some(false));
}
#[test]
fn status_bar_follows_mode_when_enabled() {
let config = RouterConfig {
status_enabled: true,
..RouterConfig::default()
};
let mut router = router_with(config);
let recorder = RecordingStatus::default();
router.set_status_sink(Box::new(recorder.clone()));
// 中文 → 英文:各刷一次;会话关掉(应用退出)不收;切成别的输入法才收起。
router.handle(ClientMessage::ModeChanged {
session: SESSION,
english: false,
});
router.handle(ClientMessage::ModeChanged {
session: SESSION,
english: true,
});
router.handle(ClientMessage::CloseSession { session: SESSION });
assert_eq!(
recorder.calls(),
vec![Some("中".to_owned()), Some("英".to_owned())]
);
router.handle(ClientMessage::ImeSwitched { session: SESSION });
assert_eq!(recorder.calls().last(), Some(&None));
}
#[test]
fn status_bar_shows_shuangpin_scheme_in_chinese() {
let config = RouterConfig {
status_enabled: true,
scheme: Scheme::Shuangpin(ShuangpinScheme::Xiaohe),
..RouterConfig::default()
};
let mut router = router_with(config);
let recorder = RecordingStatus::default();
router.set_status_sink(Box::new(recorder.clone()));
router.handle(ClientMessage::ModeChanged {
session: SESSION,
english: false,
});
assert_eq!(recorder.calls(), vec![Some("中 · 小鹤双拼".to_owned())]);
}
#[test]
fn status_bar_stays_hidden_when_disabled() {
let mut router = router();
let recorder = RecordingStatus::default();
router.set_status_sink(Box::new(recorder.clone()));
router.handle(ClientMessage::ModeChanged {
session: SESSION,
english: false,
});
assert_eq!(recorder.calls(), vec![None]);
}
#[test]
fn indicator_menu_toggles_status_bar() {
use qingjian_platform::protocol::IndicatorCommand;
let mut router = router();
let recorder = RecordingStatus::default();
router.set_status_sink(Box::new(recorder.clone()));
router.handle(ClientMessage::ModeChanged {
session: SESSION,
english: false,
});
// 任务栏图标菜单里点「悬浮状态条」:关着的打开,再点收起。
let toggle = ClientMessage::Indicator {
session: SESSION,
command: IndicatorCommand::ToggleStatusBar,
};
router.handle(toggle.clone());
assert_eq!(recorder.calls().last(), Some(&Some("中".to_owned())));
router.handle(toggle);
assert_eq!(recorder.calls().last(), Some(&None));
}
#[test]
fn mode_is_shared_by_every_app() {
let config = RouterConfig {
status_enabled: true,
..RouterConfig::default()
};
let mut router = router_with(config);
let recorder = RecordingStatus::default();
router.set_status_sink(Box::new(recorder.clone()));
let other_app = SessionId(2);
// 一个应用里切到英文:别的应用、之后新开的应用来取都是英文。
router.handle(ClientMessage::ModeChanged {
session: SESSION,
english: true,
});
assert_eq!(synced_mode(&mut router, other_app), Some(true));
assert_eq!(synced_mode(&mut router, SessionId(3)), Some(true));
// 切成别的输入法收起状态条;再有应用来取模式(又切回青简)就重新显示,模式照旧。
router.handle(ClientMessage::ImeSwitched { session: SESSION });
assert_eq!(recorder.calls().last(), Some(&None));
assert_eq!(synced_mode(&mut router, other_app), Some(true));
assert_eq!(recorder.calls().last(), Some(&Some("英".to_owned())));
}
/// 会话取一次 `SyncMode`,返回它拿到的全局模式。
fn synced_mode(router: &mut Router, session: SessionId) -> Option<bool> {
match router.handle(ClientMessage::SyncMode { session }) {
Some(ServerMessage::ModeSync { english, .. }) => english,
other => panic!("SyncMode 应回 ModeSync,实际 {other:?}"),
}
}