- 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
124 lines
3.8 KiB
Plaintext
124 lines
3.8 KiB
Plaintext
// AGENTS.md §7 message card + §34 MessageItem shape.
|
|
//
|
|
// `export enum Priority` and `export struct MessageItem` are the single source
|
|
// of truth for the message domain; Slint generates matching Rust types under
|
|
// the same names (used by `src/models.rs::mock_messages`). C2 wires them
|
|
// into `ui/message-list.slint`; C3+ leaves the card body untouched but may
|
|
// adjust the trailing "open-link" button copy.
|
|
|
|
export enum Priority {
|
|
Low,
|
|
Normal,
|
|
High,
|
|
Max,
|
|
}
|
|
|
|
// Slint does not support `Option<String>`; the URL field is split into a
|
|
// presence flag and the string itself. `has_click_url: false` + empty
|
|
// `click_url` means "no link", exactly mirroring Rust's `Option<String>`.
|
|
export struct MessageItem {
|
|
id: int,
|
|
app_id: int,
|
|
app_name: string,
|
|
title: string,
|
|
body: string,
|
|
priority: Priority,
|
|
timestamp_unix: int,
|
|
relative_time: string,
|
|
has_click_url: bool,
|
|
click_url: string,
|
|
}
|
|
|
|
// Color tokens are Dark defaults. C3 expands `MessageCard` callers to forward
|
|
// the MainWindow palette when light/system themes land.
|
|
export component MessageCard inherits Rectangle {
|
|
in property <MessageItem> item;
|
|
in property <bool> selected: false;
|
|
|
|
in property <color> bg-color: #28292d;
|
|
in property <color> bg-selected-color: #2f3035;
|
|
in property <color> card-border: #3a3b40;
|
|
in property <color> text-primary-color: #e6e6e6;
|
|
in property <color> text-secondary-color: #a0a0a0;
|
|
in property <color> text-muted-color: #6a6a6a;
|
|
in property <color> priority-low-color: #6a6a6a;
|
|
in property <color> priority-normal-color: #4ea1ff;
|
|
in property <color> priority-high-color: #ff9800;
|
|
in property <color> priority-max-color: #f44336;
|
|
in property <color> accent-color: #4ea1ff;
|
|
|
|
callback clicked();
|
|
|
|
width: 100%;
|
|
height: 76px;
|
|
border-radius: 8px;
|
|
background: selected ? bg-selected-color : bg-color;
|
|
border-color: card-border;
|
|
border-width: 1px;
|
|
|
|
HorizontalLayout {
|
|
padding: 16px;
|
|
spacing: 12px;
|
|
alignment: center;
|
|
|
|
// §7 priority indicator — small colored dot, NOT a saturated card.
|
|
Rectangle {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 4px;
|
|
background: item.priority == Priority.Low ? priority-low-color :
|
|
item.priority == Priority.Normal ? priority-normal-color :
|
|
item.priority == Priority.High ? priority-high-color :
|
|
priority-max-color;
|
|
}
|
|
|
|
VerticalLayout {
|
|
horizontal-stretch: 1;
|
|
spacing: 2px;
|
|
|
|
Text {
|
|
text: item.title != "" ? item.title : item.body;
|
|
color: text-primary-color;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
overflow: elide;
|
|
horizontal-alignment: left;
|
|
}
|
|
|
|
Text {
|
|
text: item.app_name + " · " + item.body;
|
|
color: text-secondary-color;
|
|
font-size: 12px;
|
|
overflow: elide;
|
|
horizontal-alignment: left;
|
|
}
|
|
}
|
|
|
|
// Right column — relative time + optional link icon.
|
|
VerticalLayout {
|
|
spacing: 4px;
|
|
alignment: center;
|
|
|
|
Text {
|
|
text: item.relative_time;
|
|
color: text-muted-color;
|
|
font-size: 11px;
|
|
horizontal-alignment: right;
|
|
}
|
|
|
|
// Tiny chevron glyph to hint at clickability; only when a URL is present.
|
|
if item.has_click_url: Text {
|
|
text: "↗";
|
|
color: accent-color;
|
|
font-size: 12px;
|
|
horizontal-alignment: right;
|
|
}
|
|
}
|
|
}
|
|
|
|
TouchArea {
|
|
clicked => {
|
|
root.clicked();
|
|
}
|
|
}
|
|
} |