Files
msime-windows/server/tests/src/test_candidate_wheel_paging.cpp
T
fanluskyandCarry 63eab2f88f refactor(server): 滚轮翻页复用键盘翻页逻辑并移出按键热路径
在 #351 的基础上收敛几处实现:

- CloseContextMenu 里新增的 hover 清理和 Present 原本无条件执行,而
  ShowFromGlobalState 和 Hide 每次按键都会调它。没有菜单时按键路径上会多出
  一次整屏重绘,画的还是更新前的旧场景。改为只在确实有 popup 要销毁时才做。
- 分页与动态扩充的逻辑从键盘路径复制了一份。抽成
  ExpandCandidatesKeepingPagePosition 和 MoveCandidatePage,键盘与滚轮共用。
- 每一格滚轮直接投一个任务,而每个任务都可能回引擎和用户词库。改为在队列里
  合并成 page_steps,一次快速滚动只重绘一次。
- 窗口层不再直接调 EnqueueCandidateUiAction,改为与其它候选窗动作一致,
  PostMessage 到宿主 WndProc(WM_PAGE_CANDIDATE)。EnqueueCandidateUiAction
  的序号校验随之恢复原样。
- 滚轮累加拆成纯函数 CandidateWheel::ConsumeWheelDelta 并补上单元测试;
  候选窗隐藏时忽略迟到的滚轮消息。

滚轮翻页仍然把页内高亮重置为首项,键盘翻页仍然保留原有的页内索引。

Co-authored-by: Carry <xu762556@outlook.com>
2026-09-13 00:16:18 +08:00

77 lines
2.4 KiB
C++

#include "tests/includes/test_framework.h"
#include "window/candidate_wheel_paging.h"
namespace
{
constexpr int kNotch = 120; // WHEEL_DELTA
} // namespace
TEST_CASE(candidate_wheel_emits_one_step_per_full_notch)
{
int accumulator = 0;
const auto up = CandidateWheel::ConsumeWheelDelta(accumulator, kNotch, kNotch);
REQUIRE_EQ(up.page_up, 1);
REQUIRE_EQ(up.page_down, 0);
REQUIRE_EQ(accumulator, 0);
const auto down = CandidateWheel::ConsumeWheelDelta(accumulator, -kNotch, kNotch);
REQUIRE_EQ(down.page_up, 0);
REQUIRE_EQ(down.page_down, 1);
REQUIRE_EQ(accumulator, 0);
}
TEST_CASE(candidate_wheel_accumulates_high_precision_deltas_across_messages)
{
int accumulator = 0;
// A high-precision mouse reports fractions of a notch. None of these may
// page on their own; the sixth completes the first full notch.
for (int i = 0; i < 5; ++i)
{
const auto partial = CandidateWheel::ConsumeWheelDelta(accumulator, 20, kNotch);
REQUIRE_EQ(partial.page_up, 0);
REQUIRE_EQ(partial.page_down, 0);
}
const auto completing = CandidateWheel::ConsumeWheelDelta(accumulator, 20, kNotch);
REQUIRE_EQ(completing.page_up, 1);
REQUIRE_EQ(accumulator, 0);
}
TEST_CASE(candidate_wheel_reports_every_notch_of_a_single_burst)
{
int accumulator = 0;
// A flick can arrive as one oversized delta. Every notch in it has to be
// reported, or a fast scroll silently loses pages.
const auto burst = CandidateWheel::ConsumeWheelDelta(accumulator, -3 * kNotch - 40, kNotch);
REQUIRE_EQ(burst.page_down, 3);
REQUIRE_EQ(burst.page_up, 0);
REQUIRE_EQ(accumulator, -40);
}
TEST_CASE(candidate_wheel_drops_the_remainder_when_direction_reverses)
{
int accumulator = 0;
CandidateWheel::ConsumeWheelDelta(accumulator, 80, kNotch);
REQUIRE_EQ(accumulator, 80);
// Without the reset the leftover +80 would cancel most of the first notch
// back down, so the reversal would need an extra notch to take effect.
const auto reversed = CandidateWheel::ConsumeWheelDelta(accumulator, -kNotch, kNotch);
REQUIRE_EQ(reversed.page_down, 1);
REQUIRE_EQ(reversed.page_up, 0);
REQUIRE_EQ(accumulator, 0);
}
TEST_CASE(candidate_wheel_ignores_a_non_positive_notch)
{
int accumulator = 0;
const auto steps = CandidateWheel::ConsumeWheelDelta(accumulator, kNotch, 0);
REQUIRE_EQ(steps.page_up, 0);
REQUIRE_EQ(steps.page_down, 0);
REQUIRE_EQ(accumulator, 0);
}