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:
2026-09-19 20:27:15 +08:00
parent d2de452c53
commit 383bb404a9
8 changed files with 216 additions and 230 deletions
+29 -57
View File
@@ -1,28 +1,14 @@
// AGENTS.md §10 / §11 settings page.
//
// C3 lays out the five sections (Connection / Notifications / Startup /
// Appearance / Advanced) with controls. The Appearance section is the only
// one wired back to the model: the radio selection drives
// `MainWindow.theme` via the `appearance-changed` callback. Everything else
// is a UI shell — Phase 5 wires it to `AppConfig` + Settings → state.json.
//
// Sub-components MUST be defined before the components that reference them
// in Slint 1.x, so `SettingsToggleRow` / `AppearanceRadio` come first.
// Phase 7 C4: 全部翻译为简体中文。
import { SectionLabel } from "./components.slint";
import { ServerCard } from "./server-card.slint";
// ─── Sub-components ────────────────────────────────────────────────
export component SettingsToggleRow inherits Rectangle {
in property <string> label;
// `in-out` so the TouchArea can flip it before emitting the
// toggled(label, new-value) event.
in-out property <bool> value: false;
in property <bool> enabled: true;
// Phase 6 C2: pass the row's own label + the new value to the
// caller so a single callback on the page can dispatch to the
// right AppConfig field.
callback toggled(string, bool);
in property <color> row-bg: #28292d;
@@ -122,38 +108,26 @@ export component AppearanceRadio inherits Rectangle {
}
}
// ─── Main page ─────────────────────────────────────────────────────
export component SettingsPage inherits Rectangle {
// Appearance (live wired)
in-out property <int> appearance-index: 2;
callback appearance-changed(int);
// Notifications (Phase 6 C2: live wired through setting-toggled)
in-out property <bool> desktop-notify: true;
in-out property <bool> sound: true;
in-out property <bool> notify-high: true;
in-out property <bool> notify-max: true;
// Startup (Phase 6 C2: live wired)
in-out property <bool> start-with-windows: false;
in-out property <bool> start-minimized: false;
in-out property <bool> close-to-tray: true;
// Advanced (collapsed by default per §10)
in-out property <bool> advanced-expanded: false;
// Connection (Phase 3 C2)
in-out property <bool> test-in-progress: false;
callback test-connection-requested();
// Phase 6 C2: a single callback that covers every toggle row on
// this page. Rust receives `(label, new_value)` and routes to the
// correct AppConfig field (and, for "start_with_windows", to the
// HKCU Run registry).
callback toggle-persist(string, bool);
// Tokens
in property <color> divider-color: #3a3b40;
in property <color> hint-color: #6a6a6a;
@@ -163,13 +137,11 @@ export component SettingsPage inherits Rectangle {
padding: 24px;
spacing: 24px;
// CONNECTION
// 连接
VerticalLayout {
spacing: 8px;
SectionLabel { label: "CONNECTION"; text-color: hint-color; }
SectionLabel { label: "连接"; text-color: hint-color; }
// "Test Connection" button — Phase 3 C2 wires this to the
// real `GotifyApi::health` round-trip.
Rectangle {
width: 160px;
height: 32px;
@@ -178,7 +150,7 @@ export component SettingsPage inherits Rectangle {
Text {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
text: root.test-in-progress ? "Testing..." : "Test Connection";
text: root.test-in-progress ? "测试中…" : "测试连接";
color: #e6e6e6;
font-size: 12px;
}
@@ -204,12 +176,12 @@ export component SettingsPage inherits Rectangle {
}
}
// NOTIFICATIONS
// 通知
VerticalLayout {
spacing: 10px;
SectionLabel { label: "NOTIFICATIONS"; text-color: hint-color; }
SectionLabel { label: "通知"; text-color: hint-color; }
SettingsToggleRow {
label: "Enable desktop notification";
label: "启用桌面通知";
value: root.desktop-notify;
toggled(l, v) => {
root.desktop-notify = v;
@@ -217,7 +189,7 @@ export component SettingsPage inherits Rectangle {
}
}
SettingsToggleRow {
label: "Play sound";
label: "播放声音";
value: root.sound;
enabled: root.desktop-notify;
toggled(l, v) => {
@@ -226,7 +198,7 @@ export component SettingsPage inherits Rectangle {
}
}
SettingsToggleRow {
label: "Notify on High priority";
label: "高级别时通知";
value: root.notify-high;
enabled: root.desktop-notify;
toggled(l, v) => {
@@ -235,7 +207,7 @@ export component SettingsPage inherits Rectangle {
}
}
SettingsToggleRow {
label: "Notify on Max priority";
label: "最高级别时通知";
value: root.notify-max;
enabled: root.desktop-notify;
toggled(l, v) => {
@@ -245,12 +217,12 @@ export component SettingsPage inherits Rectangle {
}
}
// STARTUP
// 开机启动
VerticalLayout {
spacing: 10px;
SectionLabel { label: "STARTUP"; text-color: hint-color; }
SectionLabel { label: "开机启动"; text-color: hint-color; }
SettingsToggleRow {
label: "Start with Windows";
label: "随 Windows 一起启动";
value: root.start-with-windows;
toggled(l, v) => {
root.start-with-windows = v;
@@ -258,7 +230,7 @@ export component SettingsPage inherits Rectangle {
}
}
SettingsToggleRow {
label: "Start minimized to tray";
label: "启动时最小化到托盘";
value: root.start-minimized;
toggled(l, v) => {
root.start-minimized = v;
@@ -266,7 +238,7 @@ export component SettingsPage inherits Rectangle {
}
}
SettingsToggleRow {
label: "Close button minimizes to tray";
label: "关闭按钮最小化到托盘";
value: root.close-to-tray;
toggled(l, v) => {
root.close-to-tray = v;
@@ -275,17 +247,17 @@ export component SettingsPage inherits Rectangle {
}
}
// APPEARANCE (live wired)
// 外观
VerticalLayout {
spacing: 10px;
SectionLabel { label: "APPEARANCE"; text-color: hint-color; }
SectionLabel { label: "外观"; text-color: hint-color; }
HorizontalLayout {
spacing: 12px;
AppearanceRadio {
label: "System";
description: "Follow Windows theme";
label: "跟随系统";
description: "跟随 Windows 主题";
selected: root.appearance-index == 0;
chosen => {
root.appearance-index = 0;
@@ -293,8 +265,8 @@ export component SettingsPage inherits Rectangle {
}
}
AppearanceRadio {
label: "Light";
description: "Bright neutrals";
label: "浅色";
description: "明亮中性色";
selected: root.appearance-index == 1;
chosen => {
root.appearance-index = 1;
@@ -302,8 +274,8 @@ export component SettingsPage inherits Rectangle {
}
}
AppearanceRadio {
label: "Dark";
description: "Graphite";
label: "深色";
description: "石墨灰";
selected: root.appearance-index == 2;
chosen => {
root.appearance-index = 2;
@@ -313,13 +285,13 @@ export component SettingsPage inherits Rectangle {
}
SettingsToggleRow {
label: "Compact message density";
label: "紧凑消息密度";
value: false;
enabled: false;
}
}
// ADVANCED (collapsed by default per §10)
// 高级(默认折叠,见 §10)
VerticalLayout {
spacing: 10px;
@@ -332,7 +304,7 @@ export component SettingsPage inherits Rectangle {
font-size: 11px;
}
Text {
text: "ADVANCED";
text: "高级";
color: hint-color;
font-size: 11px;
font-weight: 700;
@@ -347,17 +319,17 @@ export component SettingsPage inherits Rectangle {
if advanced-expanded: VerticalLayout {
spacing: 10px;
SettingsToggleRow {
label: "Log level: Info (release default)";
label: "日志级别:Info(release 默认)";
value: false;
enabled: false;
}
SettingsToggleRow {
label: "Clear local state (messages + window size)";
label: "清除本地状态(消息 + 窗口大小)";
value: false;
enabled: false;
}
SettingsToggleRow {
label: "Reset settings to defaults";
label: "恢复默认设置";
value: false;
enabled: false;
}