feat(ui): Phase 2 C2 — Mock 消息模型 + Message list + Empty state
- ui/message-card.slint: export enum Priority + struct MessageItem + MessageCard 组件(§7 优先级圆点 + 标题 + 应用·预览 + 相对时间 + URL 指示) - ui/message-list.slint: VerticalLayout + for 渲染 MessageCard (避开 std-widgets 的 include_paths 配置需求) - ui/empty-state.slint: §9 ✦ glyph(Path 自绘)+ "You're all caught up" + Test Notification 按钮 - ui/app.slint: messages.length 切换 list / empty-state;点击回 调到 MainWindow 顶层 callback - src/models.rs: mock_messages() 生成 12 条覆盖 4 个 priority 的 假消息;format_relative() 输出 "2 min ago" / Yesterday 等 - src/lib.rs: ui.set_messages(mock_messages_model()) 装载;wire on_test_notification_clicked / on_message_card_clicked 避坑: - Slint 1.18 不允许覆盖 Rectangle 内建 border-color,命名用 card-border - ModelRc::from 需要 Rc<VecModel<T>> 不是裸 VecModel 验证:cargo build / build --release 都 exit 0,0 warning - debug 26.55 MB, subsystem = WINDOWS_CUI - release 7.94 MB, subsystem = WINDOWS_GUI
This commit is contained in:
+32
-6
@@ -7,6 +7,9 @@
|
||||
|
||||
import { AppearanceMode } from "./theme.slint";
|
||||
import { NavItem, SectionLabel } from "./components.slint";
|
||||
import { MessageItem } from "./message-card.slint";
|
||||
import { MessageList } from "./message-list.slint";
|
||||
import { EmptyState } from "./empty-state.slint";
|
||||
|
||||
export enum SidebarSelection {
|
||||
All,
|
||||
@@ -48,6 +51,18 @@ export component MainWindow inherits Window {
|
||||
|
||||
in-out property <SidebarSelection> selection: SidebarSelection.All;
|
||||
|
||||
// Phase 2 C2: mock-driven message list + selection state. Set from Rust
|
||||
// via `ui.set_messages(model)` in `run()`. The list / empty-state switch
|
||||
// is purely driven by `messages.length` (Slint's `for` over an empty
|
||||
// model is a no-op).
|
||||
in-out property <[MessageItem]> messages;
|
||||
in-out property <int> selected-message-index: -1;
|
||||
|
||||
// Hooks that Phase 6 (Windows Integration) wires to a real Windows
|
||||
// toast. C2 logs them so the click path is observable end-to-end.
|
||||
callback test-notification-clicked();
|
||||
callback message-card-clicked(int);
|
||||
|
||||
HorizontalLayout {
|
||||
// §7 Sidebar width: 220-240px.
|
||||
Rectangle {
|
||||
@@ -133,18 +148,29 @@ export component MainWindow inherits Window {
|
||||
|
||||
if root.selection == SidebarSelection.All: VerticalLayout {
|
||||
padding: 24px;
|
||||
spacing: 8px;
|
||||
spacing: 12px;
|
||||
|
||||
Text {
|
||||
text: "Messages";
|
||||
text: messages.length == 0 ? "Messages" : "Messages · " + messages.length;
|
||||
color: text-primary;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
Text {
|
||||
text: "Message list and empty state land in Phase 2 C2.";
|
||||
color: text-muted;
|
||||
font-size: 12px;
|
||||
|
||||
if messages.length > 0: MessageList {
|
||||
messages: messages;
|
||||
selected-index: root.selected-message-index;
|
||||
height: parent.height - 48px;
|
||||
message-clicked(idx) => {
|
||||
root.selected-message-index = idx;
|
||||
root.message-card-clicked(idx);
|
||||
}
|
||||
}
|
||||
|
||||
if messages.length == 0: EmptyState {
|
||||
test-notification => {
|
||||
root.test-notification-clicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user