fix(ui): Phase 7 C4 — 卡片间距修复 + 全 UI 翻译为简体中文
两个问题修一处:\n\n1. 布局 bug:filter 切到 Max/High/Normal/Low 后可见卡片之间留出巨大空白。原因:Slint isible: false 在 for 循环里没真正折叠 layout 槽位,被隐藏的卡片仍占 76px 高 + 8px spacing。修法:把过滤从 Slint 侧搬到 Rust 侧,新增 filtered_model:UI 绑 filtered_model,shared_model 作为全量 canonical;切换过滤时 rebuild_filtered_model + WS drain 用 push_message_to_models 同步两表;测试连接 / tray restart 也都双表更新。\n\n2. UI 默认中文:sidebar(MESSAGES/SERVERS/FILTERS/APP → 消息/服务器/过滤/应用;All/Max/High/Normal/Low/Settings/About → 全部/最高/高/普通/低/设置/关于)、详情面板(Open link → 打开链接)、空态(You're all caught up → 暂时没有新消息;Test Notification → 发送测试通知)、设置页(5 个分组 + 所有 toggle + Appearance radio)、AboutDialog、托盘菜单(打开/暂停通知/设置.../重新连接/退出/新消息:N)、状态文本(Disconnected → 未连接;mock feed → 模拟数据;messages → 条消息)。\n\n新增 priority-filter-changed(int) 回调,sidebar 切过滤时通知 Rust 重建 filtered model。SharedModel/FilteredModel 同步逻辑:HTTP catch-up → WS drain push → 测试连接 → tray restart 都用 helper 函数维护一致性。
This commit is contained in:
+9
-28
@@ -1,30 +1,21 @@
|
||||
// AGENTS.md §7 / §37 message list.
|
||||
//
|
||||
// Phase 2 C2 uses a plain `VerticalLayout` + `for` to render the cards;
|
||||
// Slint's layout is already virtualized for off-screen items, and we keep
|
||||
// the dependency footprint minimal until Phase 6 introduces the real
|
||||
// std-widgets-backed ListView (which requires an explicit `import` from
|
||||
// `@std-widgets.slint` and an `include_paths` config in build.rs).
|
||||
// Phase 7 C4: 重写。原来的 `visible` 谓词在 `for` 循环里没有真正折叠
|
||||
// 被过滤掉的卡片——它们仍然占着 layout 槽位,导致 Normal / High /
|
||||
// Low 过滤后卡片之间出现巨大的空白(参见用户截图)。
|
||||
//
|
||||
// Phase 7 C3: a `priority-filter` property hides non-matching cards via
|
||||
// `visible`. Hiding (vs filtering the model) keeps the original index
|
||||
// intact, which lets `selected-index` stay correct when the filter
|
||||
// changes — clicking item 5 with the filter still applies to row 5
|
||||
// of the underlying list, not the post-filter row.
|
||||
// 修法:把过滤移到 Rust 侧(lib.rs::apply_priority_filter),每次过滤
|
||||
// 切换都重新构建一个 filtered VecModel,MessageList 直接绑定它,
|
||||
// 不再做 UI 侧过滤。这样还顺手解决了 AGENTS.md §37 的"100/500/1000 条
|
||||
// 消息滚动性能"问题——filtered model 仍然只包含匹配项,for 循环
|
||||
// 渲染的元素数量就是用户实际看到的数量。
|
||||
|
||||
import { MessageItem, MessageCard, Priority } from "./message-card.slint";
|
||||
import { MessageItem, MessageCard } from "./message-card.slint";
|
||||
|
||||
export component MessageList inherits Rectangle {
|
||||
in property <[MessageItem]> messages;
|
||||
in-out property <int> selected-index: -1;
|
||||
|
||||
// 0 = show all (no filter). 1 = Priority::Low, 2 = Normal,
|
||||
// 3 = High, 4 = Max. Off-by-one alignment with the Slint
|
||||
// `Priority` enum (Low=0, Normal=1, High=2, Max=3) is
|
||||
// intentional so the UI shows "All / Low / Normal / High / Max"
|
||||
// while the wire-level enum keeps its 0..3 range.
|
||||
in property <int> priority-filter: 0;
|
||||
|
||||
in property <color> card-border: #3a3b40;
|
||||
|
||||
callback message-clicked(int);
|
||||
@@ -37,16 +28,6 @@ export component MessageList inherits Rectangle {
|
||||
for item[idx] in messages: MessageCard {
|
||||
item: item;
|
||||
selected: idx == root.selected-index;
|
||||
// Show every card when filter is "All"; otherwise only
|
||||
// the priority bucket the user picked. Slint lets us
|
||||
// compare an enum directly against an int literal, so
|
||||
// `item.priority == 0` matches Priority::Low without
|
||||
// any explicit conversion.
|
||||
visible: root.priority-filter == 0
|
||||
|| (root.priority-filter == 1 && item.priority == Priority.Low)
|
||||
|| (root.priority-filter == 2 && item.priority == Priority.Normal)
|
||||
|| (root.priority-filter == 3 && item.priority == Priority.High)
|
||||
|| (root.priority-filter == 4 && item.priority == Priority.Max);
|
||||
card-border: card-border;
|
||||
clicked => {
|
||||
root.selected-index = idx;
|
||||
|
||||
Reference in New Issue
Block a user