Commit Graph
11 Commits
Author SHA1 Message Date
fanlusky 3135af32ea fix(ui): 菜单项回调不再读已释放的闭包
在 D2D 候选框上右键删除一个候选,server 进程必定闪退重启,
0xc0000005 访问违例。

MenuFlyoutItem::OnMouseUp 直接调 onClick_()。候选框右键菜单的四个
动作都是「先 CloseContextMenu(true) 再 PostMessageW」的形状,而
CloseContextMenu 会同步 RemovePopup 并 reset 掉 contextMenu,也就是
在 operator() 还在栈上时销毁了这个 MenuFlyoutItem,连带销毁正在执行
的 std::function —— 小闭包按 MSVC 的 small-object optimization 就存
在 MenuFlyoutItem 那个堆块里。紧接着的 RestoreHostAfterMenu() 同步
走 SetWindowPos,派发 WM_SIZE/WM_PAINT 触发重新分配,把那块内存回收
掉;lambda 体回来再读 this 时读到的已经是别人的数据,mov rcx,[rcx+8]
(hwnd_)就炸在这里。

四个动作形状相同,都有这个缺陷;置顶碰巧不崩只是因为 MSVC 没有在那
一处重新从闭包存储里加载捕获,不是它安全。

改法是调用前把 ClickHandler 拷到栈上,让可调用对象活过自己这次调用。
Button::OnClick 是同样的形状,一并改掉。

回归测试不去捕捉内存破坏——能不能复现取决于分配器是否恰好回收了那一
块、以及编译器有没有重新加载捕获,而且读已释放内存本身就是未定义行为。
改为断言修复所建立的不变式:句柄在自己 operator() 执行期间始终存活。
2026-09-19 15:45:25 +08:00
fanlusky ee5b50133f fix(ui): 竖排候选窗选中项高亮铺满整行
d2d 渲染下,竖排候选窗被卡片最小宽度撑大时,候选项的自然宽度小于卡片内宽,
选中高亮与命中区域只覆盖到文字宽度,右侧留下大片空白(候选项较长、宽度超过
卡片最小宽度时表现正常)。

根因:候选项行矩形取自按自然宽度测量得到的 itemGeometry。列表虽被拉伸到卡片
内宽,但绘制前 Present 阶段会以窗口宽度重新测量,把每项宽度还原为自然宽度,而
此时排布命中缓存(矩形未变)被跳过,铺满逻辑无法重新生效。

改为在 ItemRect 中直接以列表最终布局宽度 bounds_ 计算竖排行矩形,不再依赖逐项
自然宽度,绘制与命中同时受益,且不受测量/排布缓存影响。右侧留白由卡片内边距
提供,与左侧保持一致。横排路径不变。

新增 GetItemBounds 访问器与三个回归测试,其中一个复刻了 Present 重新测量的时序,
可捕获此前方案的退化。
2026-09-14 02:04:58 +08:00
古明四季 42bfd9fc49 fix(ui): 修复候选列表溢出与换行后的点击区域 2026-09-11 23:34:57 +08:00
Neptrue_Lin 2b6bfafeec Merge pull request #327 from ouchihao/codex/fix-295-retained-dpi
fix(ui): refresh DPI when reusing render targets
2026-09-11 20:34:43 +08:00
Ouchihao f3fcd322e9 fix(ui): refresh DPI when reusing render targets 2026-09-11 19:35:07 +08:00
Ouchihao f9a021dc02 fix(ui): collapse text selections before moving the caret 2026-09-11 19:32:34 +08:00
Aegon8719 dea9292814 issue-55 候选窗支持可配置的有序回落字体 2026-09-09 18:03:56 +08:00
fanlusky 43555460db fix(server): fall back to text when the icon font lacks the glyph
The floating toolbar and the emoji panel hardcoded "Segoe Fluent Icons",
which ships with Windows 11 only. DirectWrite substitutes a font silently
instead of failing, so on Windows 10 every private-use codepoint the toolbar
draws (U+E982, U+F110, U+F138, ...) rendered as a blank box: the buttons
still worked but no longer showed any state.

Resolve the font per codepoint rather than per family, because "Segoe MDL2
Assets" is a subset of Segoe Fluent Icons and swapping the family alone would
still draw tofu wherever a glyph is missing:
- add msimeui::ResolveIconGlyph(), which probes the installed icon fonts with
  IDWriteFont::HasCharacter() and returns the first family that really carries
  the glyph, or no family at all;
- give every toolbar and tab icon a text label to fall back on (中/英, 半/全,
  简/繁, ...) when neither font has the glyph;
- drop the unused ToolbarIconButton::SetGlyph/SetLabel, which would bypass the
  resolved family.

Add unit tests for the resolver, covering the missing-glyph path that cannot
be reached on a host that has Segoe Fluent Icons installed.
2026-09-08 12:41:01 +08:00
Ouchihao 3fc7ba295f feat(server): show translations in horizontal candidate windows 2026-09-06 18:09:01 +08:00
Evan Hu 210c38faff test: fail an empty suite and report how many tests ran
CI 日志里 ctest 只显示「1 个测试通过」,因为它把整个套件当作一个测试项,而 --output-on-failure 只在失败时打印用例明细。这样一个一个用例都没注册的套件,看起来和全部通过完全一样。

runner 改为:注册数为零时直接判失败,并在结尾打印实际跑了多少个用例。ctest 改用 --verbose,让用例明细进入 CI 日志。
2026-09-05 17:23:34 +09:00
Evan Hu b8ac8e0b00 test: cover the layout core with unit tests
本仓库此前没有任何测试,CI 只能证明它编译得过。布局系统是这个框架里被所有控件依赖、也最容易在重构中被改坏的部分,而且是纯几何计算,不需要 D2D 设备即可测。

新增 13 个用例,覆盖 Visual 的测量与排列语义(margin 收缩可用尺寸并回填外框、显式尺寸覆盖测量值、min/max 夹取、可用空间封顶、测量缓存的命中与失效、Stretch/Center/Trailing 对齐定位),以及 StackPanel 与 HorizontalStackPanel 的堆叠、间距和 padding 行为。

断言值全部按 src/Layout.cpp 的现有实现推导,用于锁定当前行为,不是按我认为应该如何来写。测试框架沿用 MSIME-Server 的极简写法,但去掉了 fmt 依赖,本仓库不引入任何新依赖。

Grid、WrapPanel 和 ScrollViewer 本次未覆盖,留作后续。
2026-09-05 17:21:10 +09:00